02b3b9dc4c6118ca71f51f36673e6f561cb04ccf
[lhc/web/www.git] / www / plugins-dist / filtres_images / filtres / images_transforme.php
1 <?php
2 /***************************************************************************\
3 * SPIP, Systeme de publication pour l'internet *
4 * *
5 * Copyright (c) 2001-2019 *
6 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
7 * *
8 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
9 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
10 \***************************************************************************/
11
12 /**
13 * Toutes les fonctions image_xx de ce fichier :
14 * - prennent une image en entree
15 * - fournissent une image en sortie
16 * - sont chainables les unes derrieres les autres dans toutes les combinaisons possibles
17 *
18 * @package SPIP\FiltresImages\ImagesTransforme
19 */
20
21 if (!defined('_ECRIRE_INC_VERSION')) {
22 return;
23 }
24
25 // librairie de base du core
26 include_spip('inc/filtres_images_mini');
27
28 // 1/ Aplatir une image semi-transparente (supprimer couche alpha)
29 // en remplissant la transparence avec couleur choisir $coul.
30 // 2/ Forcer le format de sauvegarde (jpg, png, gif)
31 // pour le format jpg, $qualite correspond au niveau de compression (defaut 85)
32 // pour le format gif, $qualite correspond au nombre de couleurs dans la palette (defaut 128)
33 // pour le format png, $qualite correspond au nombre de couleur dans la palette ou si 0 a une image truecolor (defaut truecolor)
34 // attention, seul 128 est supporte en l'etat (production d'images avec palette reduite pas satisfaisante)
35 // https://code.spip.net/@image_aplatir
36 // 3/ $transparence a "true" permet de conserver la transparence (utile pour conversion GIF)
37 // https://code.spip.net/@image_aplatir
38 function image_aplatir($im, $format = 'jpg', $coul = '000000', $qualite = null, $transparence = false) {
39 if ($qualite === null) {
40 if ($format == 'jpg') {
41 $qualite = _IMG_GD_QUALITE;
42 } elseif ($format == 'png') {
43 $qualite = 0;
44 } else {
45 $qualite = 128;
46 }
47 }
48 $fonction = array('image_aplatir', func_get_args());
49 $image = _image_valeurs_trans($im, "aplatir-$format-$coul-$qualite-$transparence", $format, $fonction);
50
51 if (!$image) {
52 return ("");
53 }
54
55 include_spip('inc/filtres');
56 $couleurs = _couleur_hex_to_dec($coul);
57 $dr = $couleurs["red"];
58 $dv = $couleurs["green"];
59 $db = $couleurs["blue"];
60
61 $x_i = $image["largeur"];
62 $y_i = $image["hauteur"];
63
64 $im = $image["fichier"];
65 $dest = $image["fichier_dest"];
66
67 $creer = $image["creer"];
68
69 if ($creer) {
70 $im = @$image["fonction_imagecreatefrom"]($im);
71 imagepalettetotruecolor($im);
72 $im_ = imagecreatetruecolor($x_i, $y_i);
73 if ($image["format_source"] == "gif" and function_exists('ImageCopyResampled')) {
74 // Si un GIF est transparent,
75 // fabriquer un PNG transparent
76 // Conserver la transparence
77 @imagealphablending($im_, false);
78 @imagesavealpha($im_, true);
79 if (function_exists("imageAntiAlias")) {
80 imageAntiAlias($im_, true);
81 }
82 @ImageCopyResampled($im_, $im, 0, 0, 0, 0, $x_i, $y_i, $x_i, $y_i);
83 imagedestroy($im);
84 $im = $im_;
85 }
86
87 // allouer la couleur de fond
88 if ($transparence) {
89 @imagealphablending($im_, false);
90 @imagesavealpha($im_, true);
91 $color_t = imagecolorallocatealpha($im_, $dr, $dv, $db, 127);
92 } else {
93 $color_t = ImageColorAllocate($im_, $dr, $dv, $db);
94 }
95
96 imagefill($im_, 0, 0, $color_t);
97
98 //??
99 //$dist = abs($trait);
100
101 $transp_x = false;
102
103 if ($image["format_source"] == "jpg") {
104 $im_ = &$im;
105 } else {
106 for ($x = 0; $x < $x_i; $x++) {
107 for ($y = 0; $y < $y_i; $y++) {
108
109 $rgb = ImageColorAt($im, $x, $y);
110 $a = ($rgb >> 24) & 0xFF;
111 $r = ($rgb >> 16) & 0xFF;
112 $g = ($rgb >> 8) & 0xFF;
113 $b = $rgb & 0xFF;
114
115 $a = (127 - $a) / 127;
116
117 if ($a == 1) { // Limiter calculs
118 $r = $r;
119 $g = $g;
120 $b = $b;
121 } else {
122 if ($a == 0) { // Limiter calculs
123 $r = $dr;
124 $g = $dv;
125 $b = $db;
126
127 $transp_x = $x; // Memoriser un point transparent
128 $transp_y = $y;
129
130 } else {
131 $r = round($a * $r + $dr * (1 - $a));
132 $g = round($a * $g + $dv * (1 - $a));
133 $b = round($a * $b + $db * (1 - $a));
134 }
135 }
136 $a = (1 - $a) * 127;
137 $color = ImageColorAllocateAlpha($im_, $r, $g, $b, $a);
138 imagesetpixel($im_, $x, $y, $color);
139 }
140 }
141 }
142 // passer en palette si besoin
143 if ($format == 'gif' or ($format == 'png' and $qualite !== 0)) {
144 // creer l'image finale a palette
145 // (on recycle l'image initiale si possible, sinon on en recree une)
146 if ($im === $im_) {
147 $im = imagecreatetruecolor($x_i, $y_i);
148 }
149
150 @imagetruecolortopalette($im, true, $qualite);
151
152
153 //$im = imagecreate($x_i, $y_i);
154 // copier l'image true color vers la palette
155 imagecopy($im, $im_, 0, 0, 0, 0, $x_i, $y_i);
156 // matcher les couleurs au mieux par rapport a l'image initiale
157 // si la fonction est disponible (php>=4.3)
158 if (function_exists('imagecolormatch')) {
159 @imagecolormatch($im_, $im);
160 }
161
162 if ($format == 'gif' && $transparence && $transp_x) {
163 $color_t = ImagecolorAt($im, $transp_x, $transp_y);
164 if ($format == "gif" && $transparence) {
165 @imagecolortransparent($im, $color_t);
166 }
167 }
168
169
170 // produire le resultat
171 _image_gd_output($im, $image, $qualite);
172 } else {
173 _image_gd_output($im_, $image, $qualite);
174 }
175 if ($im !== $im_) {
176 imagedestroy($im);
177 }
178 imagedestroy($im_);
179 }
180
181 return _image_ecrire_tag($image, array('src' => $dest));
182 }
183
184
185 // Enregistrer une image dans un format donne
186 // (conserve la transparence gif, png, ico)
187 // utilise [->@image_aplatir]
188 // https://code.spip.net/@image_format
189 function image_format($img, $format = 'png') {
190 $qualite = null;
191 if ($format == 'png8') {
192 $format = 'png';
193 $qualite = 128;
194 }
195
196 return image_aplatir($img, $format, 'cccccc', $qualite, true);
197 }
198
199
200 // Transforme l'image en PNG transparent
201 // alpha = 0: aucune transparence
202 // alpha = 127: completement transparent
203 // https://code.spip.net/@image_alpha
204 function image_alpha($im, $alpha = 63) {
205 $fonction = array('image_alpha', func_get_args());
206 $image = _image_valeurs_trans($im, "alpha-$alpha", "png", $fonction);
207 if (!$image) {
208 return ("");
209 }
210
211 $x_i = $image["largeur"];
212 $y_i = $image["hauteur"];
213
214 $im = $image["fichier"];
215 $dest = $image["fichier_dest"];
216
217 $creer = $image["creer"];
218
219 if ($creer) {
220 // Creation de l'image en deux temps
221 // de facon a conserver les GIF transparents
222 $im = $image["fonction_imagecreatefrom"]($im);
223 imagepalettetotruecolor($im);
224 $im2 = imagecreatetruecolor($x_i, $y_i);
225 @imagealphablending($im2, false);
226 @imagesavealpha($im2, true);
227 $color_t = ImageColorAllocateAlpha($im2, 255, 255, 255, 127);
228 imagefill($im2, 0, 0, $color_t);
229 imagecopy($im2, $im, 0, 0, 0, 0, $x_i, $y_i);
230
231 $im_ = imagecreatetruecolor($x_i, $y_i);
232 imagealphablending($im_, false);
233 imagesavealpha($im_, true);
234
235
236 for ($x = 0; $x < $x_i; $x++) {
237 for ($y = 0; $y < $y_i; $y++) {
238 $rgb = ImageColorAt($im2, $x, $y);
239
240 if (function_exists('imagecolorallocatealpha')) {
241 $a = ($rgb >> 24) & 0xFF;
242 $r = ($rgb >> 16) & 0xFF;
243 $g = ($rgb >> 8) & 0xFF;
244 $b = $rgb & 0xFF;
245
246
247 $a_ = $alpha + $a - round($a * $alpha / 127);
248 $rgb = imagecolorallocatealpha($im_, $r, $g, $b, $a_);
249 }
250 imagesetpixel($im_, $x, $y, $rgb);
251 }
252 }
253 _image_gd_output($im_, $image);
254 imagedestroy($im_);
255 imagedestroy($im);
256 imagedestroy($im2);
257 }
258
259 return _image_ecrire_tag($image, array('src' => $dest));
260 }
261
262 /**
263 * Recadre (rogne) une image en indiquant la taille de la découpe souhaitée
264 *
265 * On peut indiquer une proportion ou une taille spécifique, une position de rognage
266 * et une couleur de fond, si le rognage est de taille plus grande que l'image d'origine.
267 *
268 * @example
269 * - `[(#FICHIER|image_recadre{800, 400})]`
270 * - `[(#FICHIER|image_recadre{800, 400, center})]`
271 * - `[(#FICHIER|image_recadre{800, 400, center, black})]`
272 * - `[(#FICHIER|image_recadre{16:9})]`
273 * - `[(#FICHIER|image_recadre{16:9, -})]` (- est appliqué par défaut, équivalent à image_passe_partout)
274 * - `[(#FICHIER|image_recadre{16:9, +, center, white})]`
275 * - `[(#FICHIER|image_recadre{16:9, -, top left})]`
276 * - `[(#FICHIER|image_recadre{16:9, -, top=40 left=20})]`
277 *
278 * @filtre
279 * @uses _image_valeurs_trans()
280 * @uses _image_tag_changer_taille() si image trop grande pour être traitée
281 * @uses _image_ecrire_tag()
282 * @link https://www.spip.net/5786
283 *
284 * @param string $im
285 * Chemin de l'image ou balise html `<img src=... />`
286 * @param string|int $width
287 * Largeur du recadrage
288 * ou ratio sous la forme "16:9"
289 * @param string|int $height
290 * Hauteur du recadrage
291 * ou "+" (agrandir) ou "-" (reduire) si un ratio est fourni pour width
292 * @param string $position
293 * Indication de position de la découpe :
294 * - `center`, `left`, `right`, `top`, `bottom`,
295 * - ou combinaisons de plusiers `top left`
296 * - ou indication en pixels depuis une position `top=50` ou composée `top=40 left=50`
297 * - ou nom d'une fonction spéciale qui calculera et retournera la position souhaitée
298 * @param string $background_color
299 * Couleur de fond si on agrandit l'image
300 * @return string
301 * balise image recadrée
302 */
303 function image_recadre($im, $width, $height, $position = 'center', $background_color = 'white') {
304 $fonction = array('image_recadre', func_get_args());
305 $image = _image_valeurs_trans($im, "recadre-$width-$height-$position-$background_color", false, $fonction);
306
307 if (!$image) {
308 return ("");
309 }
310
311 $x_i = $image["largeur"];
312 $y_i = $image["hauteur"];
313
314 if (_IMG_GD_MAX_PIXELS && $x_i * $y_i > _IMG_GD_MAX_PIXELS) {
315 spip_log("image_recadre impossible sur $im : " . $x_i * $y_i . "pixels");
316
317 // on se rabat sur une reduction CSS
318 return _image_tag_changer_taille($im, $width, $height);
319 }
320
321 // on recadre pour respecter un ratio ?
322 // width : "16:9"
323 // height : "+" pour agrandir l'image et "-" pour la croper
324 if (strpos($width, ":") !== false) {
325 list($wr, $hr) = explode(":", $width);
326 $hm = $x_i / $wr * $hr;
327 $ym = $y_i / $hr * $wr;
328 if ($height == "+" ? ($y_i < $hm) : ($y_i > $hm)) {
329 $width = $x_i;
330 $height = $hm;
331 } else {
332 $width = $ym;
333 $height = $y_i;
334 }
335 }
336
337 if ($width == 0) {
338 $width = $x_i;
339 }
340 if ($height == 0) {
341 $height = $y_i;
342 }
343
344 $offset_width = $x_i - $width;
345 $offset_height = $y_i - $height;
346 $position = strtolower($position);
347
348 // chercher une fonction spéciale de calcul des coordonnées de positionnement.
349 // exemple 'focus' ou 'focus-center' avec le plugin 'Centre Image'
350 if (!in_array($position, array('center', 'top', 'right', 'bottom', 'left'))) {
351 if (count(explode(" ", $position)) == 1) {
352 $positionner = charger_fonction("image_positionner_par_" . str_replace("-", "_", $position), "inc", true);
353 if ($positionner) {
354 $position = $positionner($im, $width, $height);
355 }
356 }
357 }
358
359 if (strpos($position, 'left') !== false) {
360 if (preg_match(';left=(\d{1}\d+);', $position, $left)) {
361 $offset_width = $left[1];
362 } else {
363 $offset_width = 0;
364 }
365 } elseif (strpos($position, 'right') !== false) {
366 $offset_width = $offset_width;
367 } else {
368 $offset_width = intval(ceil($offset_width / 2));
369 }
370
371 if (strpos($position, 'top') !== false) {
372 if (preg_match(';top=(\d{1}\d+);', $position, $top)) {
373 $offset_height = $top[1];
374 } else {
375 $offset_height = 0;
376 }
377 } elseif (strpos($position, 'bottom') !== false) {
378 $offset_height = $offset_height;
379 } else {
380 $offset_height = intval(ceil($offset_height / 2));
381 }
382
383 $im = $image["fichier"];
384 $dest = $image["fichier_dest"];
385
386 $creer = $image["creer"];
387
388 if ($creer) {
389 $im = $image["fonction_imagecreatefrom"]($im);
390 imagepalettetotruecolor($im);
391 $im_ = imagecreatetruecolor($width, $height);
392 @imagealphablending($im_, false);
393 @imagesavealpha($im_, true);
394
395 if ($background_color == 'transparent') {
396 $color_t = imagecolorallocatealpha($im_, 255, 255, 255, 127);
397 } else {
398 $bg = _couleur_hex_to_dec($background_color);
399 $color_t = imagecolorallocate($im_, $bg['red'], $bg['green'], $bg['blue']);
400 }
401 imagefill($im_, 0, 0, $color_t);
402 imagecopy($im_, $im, max(0, -$offset_width), max(0, -$offset_height), max(0, $offset_width), max(0, $offset_height),
403 min($width, $x_i), min($height, $y_i));
404
405 _image_gd_output($im_, $image);
406 imagedestroy($im_);
407 imagedestroy($im);
408 }
409
410 return _image_ecrire_tag($image, array('src' => $dest, 'width' => $width, 'height' => $height));
411 }
412
413
414 /**
415 * Recadrer une image dans le rectangle le plus petit possible sans perte
416 * de pixels non transparent
417 *
418 * @param string $im
419 * @return string
420 */
421 function image_recadre_mini($im) {
422 $fonction = array('image_recadre_mini', func_get_args());
423 $image = _image_valeurs_trans($im, "recadre_mini", false, $fonction);
424
425 if (!$image) {
426 return ("");
427 }
428
429 $width = $image["largeur"];
430 $height = $image["hauteur"];
431
432 $im = $image["fichier"];
433 $dest = $image["fichier_dest"];
434
435 $creer = $image["creer"];
436 if ($creer) {
437 $im = $image["fonction_imagecreatefrom"]($im);
438 imagepalettetotruecolor($im);
439
440 // trouver le rectangle mini qui contient des infos de couleur
441 // recherche optimisee qui ne balaye que par zone
442 $min_x = $width;
443 $min_y = $height;
444 $max_y = $max_x = 0;
445 $yy = 0;
446 while ($yy <= $height / 2 and $max_y <= $min_y) {
447 if ($yy < $min_y) {
448 for ($xx = 0; $xx < $width; $xx++) {
449 $color_index = imagecolorat($im, $xx, $yy);
450 $color_tran = imagecolorsforindex($im, $color_index);
451 if ($color_tran['alpha'] !== 127) {
452 $min_y = min($yy, $min_y);
453 $max_y = max($height - 1 - $yy, $max_y);
454 break;
455 }
456 }
457 }
458 if ($height - 1 - $yy > $max_y) {
459 for ($xx = 0; $xx < $width; $xx++) {
460 $color_index = imagecolorat($im, $xx, $height - 1 - $yy);
461 $color_tran = imagecolorsforindex($im, $color_index);
462 if ($color_tran['alpha'] !== 127) {
463 $min_y = min($yy, $min_y);
464 $max_y = max($height - 1 - $yy, $max_y);
465 break;
466 }
467 }
468 }
469 $yy++;
470 }
471 $min_y = min($max_y, $min_y); // tout a 0 aucun pixel trouve
472
473 $xx = 0;
474 while ($xx <= $width / 2 and $max_x <= $min_x) {
475 if ($xx < $min_x) {
476 for ($yy = $min_y; $yy < $max_y; $yy++) {
477 $color_index = imagecolorat($im, $xx, $yy);
478 $color_tran = imagecolorsforindex($im, $color_index);
479 if ($color_tran['alpha'] !== 127) {
480 $min_x = min($xx, $min_x);
481 $max_x = max($xx, $max_x);
482 break; // inutile de continuer sur cette colonne
483 }
484 }
485 }
486 if ($width - 1 - $xx > $max_x) {
487 for ($yy = $min_y; $yy < $max_y; $yy++) {
488 $color_index = imagecolorat($im, $width - 1 - $xx, $yy);
489 $color_tran = imagecolorsforindex($im, $color_index);
490 if ($color_tran['alpha'] !== 127) {
491 $min_x = min($width - 1 - $xx, $min_x);
492 $max_x = max($width - 1 - $xx, $max_x);
493 break; // inutile de continuer sur cette colonne
494 }
495 }
496 }
497 $xx++;
498 }
499 $min_x = min($max_x, $min_x); // tout a 0 aucun pixel trouve
500
501 $width = $max_x - $min_x + 1;
502 $height = $max_y - $min_y + 1;
503
504 $im_ = imagecreatetruecolor($width, $height);
505 @imagealphablending($im_, false);
506 @imagesavealpha($im_, true);
507
508 $color_t = imagecolorallocatealpha($im_, 255, 255, 255, 127);
509 imagefill($im_, 0, 0, $color_t);
510 imagecopy($im_, $im, 0, 0, $min_x, $min_y, $width, $height);
511
512 _image_gd_output($im_, $image);
513 imagedestroy($im_);
514 imagedestroy($im);
515 } else {
516 list($height, $width) = taille_image($image['fichier_dest']);
517 }
518
519 return _image_ecrire_tag($image, array('src' => $dest, 'width' => $width, 'height' => $height));
520 }
521
522
523 // https://code.spip.net/@image_flip_vertical
524 function image_flip_vertical($im) {
525 $fonction = array('image_flip_vertical', func_get_args());
526 $image = _image_valeurs_trans($im, "flip_v", false, $fonction);
527 if (!$image) {
528 return ("");
529 }
530
531 $x_i = $image["largeur"];
532 $y_i = $image["hauteur"];
533
534 $im = $image["fichier"];
535 $dest = $image["fichier_dest"];
536
537 $creer = $image["creer"];
538
539 if ($creer) {
540 $im = $image["fonction_imagecreatefrom"]($im);
541 imagepalettetotruecolor($im);
542 $im_ = imagecreatetruecolor($x_i, $y_i);
543 @imagealphablending($im_, false);
544 @imagesavealpha($im_, true);
545
546 $color_t = ImageColorAllocateAlpha($im_, 255, 255, 255, 127);
547 imagefill($im_, 0, 0, $color_t);
548
549 for ($x = 0; $x < $x_i; $x++) {
550 for ($y = 0; $y < $y_i; $y++) {
551 imagecopy($im_, $im, $x_i - $x - 1, $y, $x, $y, 1, 1);
552 }
553 }
554
555 _image_gd_output($im_, $image);
556 imagedestroy($im_);
557 imagedestroy($im);
558 }
559
560 return _image_ecrire_tag($image, array('src' => $dest));
561 }
562
563 // https://code.spip.net/@image_flip_horizontal
564 function image_flip_horizontal($im) {
565 $fonction = array('image_flip_horizontal', func_get_args());
566 $image = _image_valeurs_trans($im, "flip_h", false, $fonction);
567 if (!$image) {
568 return ("");
569 }
570
571 $x_i = $image["largeur"];
572 $y_i = $image["hauteur"];
573
574 $im = $image["fichier"];
575 $dest = $image["fichier_dest"];
576
577 $creer = $image["creer"];
578
579 if ($creer) {
580 $im = $image["fonction_imagecreatefrom"]($im);
581 imagepalettetotruecolor($im);
582 $im_ = imagecreatetruecolor($x_i, $y_i);
583 @imagealphablending($im_, false);
584 @imagesavealpha($im_, true);
585
586 $color_t = ImageColorAllocateAlpha($im_, 255, 255, 255, 127);
587 imagefill($im_, 0, 0, $color_t);
588
589 for ($x = 0; $x < $x_i; $x++) {
590 for ($y = 0; $y < $y_i; $y++) {
591 imagecopy($im_, $im, $x, $y_i - $y - 1, $x, $y, 1, 1);
592 }
593 }
594 _image_gd_output($im_, $image);
595 imagedestroy($im_);
596 imagedestroy($im);
597 }
598
599 return _image_ecrire_tag($image, array('src' => $dest));
600 }
601
602 // https://code.spip.net/@image_masque
603 function image_masque($im, $masque, $pos = "") {
604 // Passer, en plus de l'image d'origine,
605 // une image de "masque": un fichier PNG24 transparent.
606 // Le decoupage se fera selon la transparence du "masque",
607 // et les couleurs seront eclaircies/foncees selon de couleur du masque.
608 // Pour ne pas modifier la couleur, le masque doit etre en gris 50%.
609 //
610 // Si l'image source est plus grande que le masque, alors cette image est reduite a la taille du masque.
611 // Sinon, c'est la taille de l'image source qui est utilisee.
612 //
613 // $pos est une variable libre, qui permet de passer left=..., right=..., bottom=..., top=...
614 // dans ce cas, le masque est place a ces positions sur l'image d'origine,
615 // et evidemment cette image d'origine n'est pas redimensionnee
616 //
617 // Positionnement horizontal: text-align=left, right, center
618 // Positionnement vertical : vertical-align=top, bottom, middle
619 // (les positionnements left, right, top, left sont relativement inutiles, mais coherence avec CSS)
620 //
621 // Choix du mode de fusion: mode=masque, normal, eclaircir, obscurcir, produit, difference, ecran, superposer, lumiere_dure, teinte, saturation, valeur
622 // https://en.wikipedia.org/wiki/Blend_modes
623 // masque: mode par defaut
624 // normal: place la nouvelle image par dessus l'ancienne
625 // eclaircir: place uniquement les points plus clairs
626 // obscurcir: place uniquement les points plus fonc'es
627 // produit: multiplie par le masque (points noirs rendent l'image noire, points blancs ne changent rien)
628 // difference: remplit avec l'ecart entre les couleurs d'origine et du masque
629 // ecran: effet inverse de 'produit' -> l'image resultante est plus claire
630 // superposer: combine les modes 'produit' et 'ecran' -> les parties claires sont eclaircies, les parties sombres assombries.
631 // lumiere_dure: equivalent a 'superposer', sauf que l'image du bas et du haut sont inversees.
632 // teinte: utilise la teinte du masque
633 // saturation: utilise la saturation du masque
634 // valeur: utilise la valeur du masque
635
636 $mode = "masque";
637
638 $numargs = func_num_args();
639 $arg_list = func_get_args();
640 $variable = array();
641
642 $texte = $arg_list[0];
643 for ($i = 1; $i < $numargs; $i++) {
644 if (($p = strpos($arg_list[$i], "=")) !== false) {
645 $nom_variable = substr($arg_list[$i], 0, $p);
646 $val_variable = substr($arg_list[$i], $p + 1);
647 $variable["$nom_variable"] = $val_variable;
648 $defini["$nom_variable"] = 1;
649 }
650 }
651 if (isset($defini["mode"]) and $defini["mode"]) {
652 $mode = $variable["mode"];
653 }
654
655 // utiliser _image_valeurs_trans pour accepter comme masque :
656 // - une balise <img src='...' />
657 // - une image avec un timestamp ?01234
658 $mask = _image_valeurs_trans($masque, "source-image_masque", "png", null, true);
659 if (!$mask) {
660 return ("");
661 }
662 $masque = $mask['fichier'];
663
664 $pos = md5(serialize($variable) . $mask['date_src']);
665 $fonction = array('image_masque', func_get_args());
666 $image = _image_valeurs_trans($im, "masque-$masque-$pos", "png", $fonction);
667 if (!$image) {
668 return ("");
669 }
670
671 $x_i = $image["largeur"];
672 $y_i = $image["hauteur"];
673
674 $im = $image["fichier"];
675 $dest = $image["fichier_dest"];
676
677 $creer = $image["creer"];
678
679 // doit-on positionner l'image ?
680 $placer = false;
681 foreach (array("right", "left", "bottom", "top", "text-align", "vertical-align") as $pl) {
682 if (isset($defini[$pl]) and $defini[$pl]) {
683 $placer = true;
684 break;
685 }
686 }
687
688 if ($creer) {
689
690 $im_m = $mask["fichier"];
691 $x_m = $mask["largeur"];
692 $y_m = $mask["hauteur"];
693
694 $im2 = $mask["fonction_imagecreatefrom"]($masque);
695 if ($mask["format_source"] == "gif" and function_exists('ImageCopyResampled')) {
696 $im2_ = imagecreatetruecolor($x_m, $y_m);
697 // Si un GIF est transparent,
698 // fabriquer un PNG transparent
699 // Conserver la transparence
700 if (function_exists("imageAntiAlias")) {
701 imageAntiAlias($im2_, true);
702 }
703 @imagealphablending($im2_, false);
704 @imagesavealpha($im2_, true);
705 @ImageCopyResampled($im2_, $im2, 0, 0, 0, 0, $x_m, $y_m, $x_m, $y_m);
706 imagedestroy($im2);
707 $im2 = $im2_;
708 }
709
710 if ($placer) {
711 // On fabriquer une version "agrandie" du masque,
712 // aux dimensions de l'image source
713 // et on "installe" le masque dans cette image
714 // ainsi: aucun redimensionnement
715
716 $dx = 0;
717 $dy = 0;
718
719 if (isset($defini["right"]) and $defini["right"]) {
720 $right = $variable["right"];
721 $dx = ($x_i - $x_m) - $right;
722 }
723 if (isset($defini["bottom"]) and $defini["bottom"]) {
724 $bottom = $variable["bottom"];
725 $dy = ($y_i - $y_m) - $bottom;
726 }
727 if (isset($defini["top"]) and $defini["top"]) {
728 $top = $variable["top"];
729 $dy = $top;
730 }
731 if (isset($defini["left"]) and $defini["left"]) {
732 $left = $variable["left"];
733 $dx = $left;
734 }
735 if (isset($defini["text-align"]) and $defini["text-align"]) {
736 $align = $variable["text-align"];
737 if ($align == "right") {
738 $right = 0;
739 $dx = ($x_i - $x_m);
740 } else {
741 if ($align == "left") {
742 $left = 0;
743 $dx = 0;
744 } else {
745 if ($align = "center") {
746 $dx = round(($x_i - $x_m) / 2);
747 }
748 }
749 }
750 }
751 if (isset($defini["vertical-align"]) and $defini["vertical-align"]) {
752 $valign = $variable["vertical-align"];
753 if ($valign == "bottom") {
754 $bottom = 0;
755 $dy = ($y_i - $y_m);
756 } else {
757 if ($valign == "top") {
758 $top = 0;
759 $dy = 0;
760 } else {
761 if ($valign = "middle") {
762 $dy = round(($y_i - $y_m) / 2);
763 }
764 }
765 }
766 }
767
768
769 $im3 = imagecreatetruecolor($x_i, $y_i);
770 @imagealphablending($im3, false);
771 @imagesavealpha($im3, true);
772 if ($mode == "masque") {
773 $color_t = ImageColorAllocateAlpha($im3, 128, 128, 128, 0);
774 } else {
775 $color_t = ImageColorAllocateAlpha($im3, 128, 128, 128, 127);
776 }
777 imagefill($im3, 0, 0, $color_t);
778
779
780 imagecopy($im3, $im2, $dx, $dy, 0, 0, $x_m, $y_m);
781
782 imagedestroy($im2);
783 $im2 = imagecreatetruecolor($x_i, $y_i);
784 @imagealphablending($im2, false);
785 @imagesavealpha($im2, true);
786
787 imagecopy($im2, $im3, 0, 0, 0, 0, $x_i, $y_i);
788 imagedestroy($im3);
789 $x_m = $x_i;
790 $y_m = $y_i;
791 }
792
793
794 $rapport = $x_i / $x_m;
795 if (($y_i / $y_m) < $rapport) {
796 $rapport = $y_i / $y_m;
797 }
798
799 $x_d = ceil($x_i / $rapport);
800 $y_d = ceil($y_i / $rapport);
801
802
803 if ($x_i < $x_m or $y_i < $y_m) {
804 $x_dest = $x_i;
805 $y_dest = $y_i;
806 $x_dec = 0;
807 $y_dec = 0;
808 } else {
809 $x_dest = $x_m;
810 $y_dest = $y_m;
811 $x_dec = round(($x_d - $x_m) / 2);
812 $y_dec = round(($y_d - $y_m) / 2);
813 }
814
815
816 $nouveau = _image_valeurs_trans(image_reduire($im, $x_d, $y_d), "");
817 if (!is_array($nouveau)) {
818 return ("");
819 }
820 $im_n = $nouveau["fichier"];
821
822
823 $im = $nouveau["fonction_imagecreatefrom"]($im_n);
824 imagepalettetotruecolor($im);
825 if ($nouveau["format_source"] == "gif" and function_exists('ImageCopyResampled')) {
826 $im_ = imagecreatetruecolor($x_dest, $y_dest);
827 // Si un GIF est transparent,
828 // fabriquer un PNG transparent
829 // Conserver la transparence
830 if (function_exists("imageAntiAlias")) {
831 imageAntiAlias($im_, true);
832 }
833 @imagealphablending($im_, false);
834 @imagesavealpha($im_, true);
835 @ImageCopyResampled($im_, $im, 0, 0, 0, 0, $x_dest, $y_dest, $x_dest, $y_dest);
836 imagedestroy($im);
837 $im = $im_;
838 }
839 $im_ = imagecreatetruecolor($x_dest, $y_dest);
840 @imagealphablending($im_, false);
841 @imagesavealpha($im_, true);
842 $color_t = ImageColorAllocateAlpha($im_, 255, 255, 255, 127);
843 imagefill($im_, 0, 0, $color_t);
844
845
846 // calcul couleurs de chaque pixel selon les modes de fusion
847 for ($x = 0; $x < $x_dest; $x++) {
848 for ($y = 0; $y < $y_dest; $y++) {
849 $rgb = ImageColorAt($im2, $x, $y); // image au dessus
850 $a = ($rgb >> 24) & 0xFF;
851 $r = ($rgb >> 16) & 0xFF;
852 $g = ($rgb >> 8) & 0xFF;
853 $b = $rgb & 0xFF;
854
855 $rgb2 = ImageColorAt($im, $x + $x_dec, $y + $y_dec); // image en dessous
856 $a2 = ($rgb2 >> 24) & 0xFF;
857 $r2 = ($rgb2 >> 16) & 0xFF;
858 $g2 = ($rgb2 >> 8) & 0xFF;
859 $b2 = $rgb2 & 0xFF;
860
861 if ($mode == "normal") {
862 $v = (127 - $a) / 127;
863 if ($v == 1) {
864 $r_ = $r;
865 $g_ = $g;
866 $b_ = $b;
867 } else {
868 $v2 = (127 - $a2) / 127;
869 if ($v + $v2 == 0) {
870 $r_ = $r2;
871 $g_ = $g2;
872 $b_ = $b2;
873 } else {
874 if ($v2 == 0) {
875 $r_ = $r;
876 $g_ = $g;
877 $b_ = $b;
878 } else {
879 if ($v == 0) {
880 $r_ = $r2;
881 $g_ = $g2;
882 $b_ = $b2;
883 } else {
884 $r_ = $r + (($r2 - $r) * $v2 * (1 - $v));
885 $g_ = $g + (($g2 - $g) * $v2 * (1 - $v));
886 $b_ = $b + (($b2 - $b) * $v2 * (1 - $v));
887 }
888 }
889 }
890 }
891 $a_ = min($a, $a2);
892
893 } elseif (in_array($mode, array("produit", "difference", "superposer", "lumiere_dure", "ecran"))) {
894 if ($mode == "produit") {
895 $r = ($r / 255) * $r2;
896 $g = ($g / 255) * $g2;
897 $b = ($b / 255) * $b2;
898 } elseif ($mode == "difference") {
899 $r = abs($r - $r2);
900 $g = abs($g - $g2);
901 $b = abs($b - $b2);
902 } elseif ($mode == "superposer") {
903 $r = ($r2 < 128) ? 2 * $r * $r2 / 255 : 255 - (2 * (255 - $r) * (255 - $r2) / 255);
904 $g = ($g2 < 128) ? 2 * $g * $g2 / 255 : 255 - (2 * (255 - $g) * (255 - $g2) / 255);
905 $b = ($b2 < 128) ? 2 * $b * $b2 / 255 : 255 - (2 * (255 - $b) * (255 - $b2) / 255);
906 } elseif ($mode == "lumiere_dure") {
907 $r = ($r < 128) ? 2 * $r * $r2 / 255 : 255 - (2 * (255 - $r2) * (255 - $r) / 255);
908 $g = ($g < 128) ? 2 * $g * $g2 / 255 : 255 - (2 * (255 - $g2) * (255 - $g) / 255);
909 $b = ($b < 128) ? 2 * $b * $b2 / 255 : 255 - (2 * (255 - $b2) * (255 - $b) / 255);
910 } elseif ($mode == "ecran") {
911 $r = 255 - (((255 - $r) * (255 - $r2)) / 255);
912 $g = 255 - (((255 - $g) * (255 - $g2)) / 255);
913 $b = 255 - (((255 - $b) * (255 - $b2)) / 255);
914 }
915 $r = max(0, min($r, 255));
916 $g = max(0, min($g, 255));
917 $b = max(0, min($b, 255));
918
919 // melange en fonction de la transparence du masque
920 $v = (127 - $a) / 127;
921 if ($v == 1) { // melange complet
922 $r_ = $r;
923 $g_ = $g;
924 $b_ = $b;
925 } else {
926 $v2 = (127 - $a2) / 127;
927 if ($v + $v2 == 0) { // ??
928 $r_ = $r2;
929 $g_ = $g2;
930 $b_ = $b2;
931 } else { // pas de melange (transparence du masque)
932 $r_ = $r + (($r2 - $r) * $v2 * (1 - $v));
933 $g_ = $g + (($g2 - $g) * $v2 * (1 - $v));
934 $b_ = $b + (($b2 - $b) * $v2 * (1 - $v));
935 }
936 }
937 $a_ = $a2;
938
939 } elseif ($mode == "eclaircir" or $mode == "obscurcir") {
940 $v = (127 - $a) / 127;
941 if ($v == 1) {
942 $r_ = $r;
943 $g_ = $g;
944 $b_ = $b;
945 } else {
946 $v2 = (127 - $a2) / 127;
947 if ($v + $v2 == 0) {
948 $r_ = $r2;
949 $g_ = $g2;
950 $b_ = $b2;
951 } else {
952 $r_ = $r + (($r2 - $r) * $v2 * (1 - $v));
953 $g_ = $g + (($g2 - $g) * $v2 * (1 - $v));
954 $b_ = $b + (($b2 - $b) * $v2 * (1 - $v));
955 }
956 }
957 if ($mode == "eclaircir") {
958 $r_ = max($r_, $r2);
959 $g_ = max($g_, $g2);
960 $b_ = max($b_, $b2);
961 } else {
962 $r_ = min($r_, $r2);
963 $g_ = min($g_, $g2);
964 $b_ = min($b_, $b2);
965 }
966 $a_ = min($a, $a2);
967
968 } elseif (in_array($mode, array("teinte", "saturation", "valeur"))) {
969 include_spip("filtres/images_lib");
970 $hsv = _couleur_rgb2hsv($r, $g, $b); // image au dessus
971 $h = $hsv["h"];
972 $s = $hsv["s"];
973 $v = $hsv["v"];
974 $hsv2 = _couleur_rgb2hsv($r2, $g2, $b2); // image en dessous
975 $h2 = $hsv2["h"];
976 $s2 = $hsv2["s"];
977 $v2 = $hsv2["v"];
978 switch ($mode) {
979 case "teinte";
980 $rgb3 = _couleur_hsv2rgb($h, $s2, $v2);
981 break;
982 case "saturation";
983 $rgb3 = _couleur_hsv2rgb($h2, $s, $v2);
984 break;
985 case "valeur";
986 $rgb3 = _couleur_hsv2rgb($h2, $s2, $v);
987 break;
988 }
989 $r = $rgb3["r"];
990 $g = $rgb3["g"];
991 $b = $rgb3["b"];
992
993 // melange en fonction de la transparence du masque
994 $v = (127 - $a) / 127;
995 if ($v == 1) { // melange complet
996 $r_ = $r;
997 $g_ = $g;
998 $b_ = $b;
999 } else {
1000 $v2 = (127 - $a2) / 127;
1001 if ($v + $v2 == 0) { // ??
1002 $r_ = $r2;
1003 $g_ = $g2;
1004 $b_ = $b2;
1005 } else { // pas de melange (transparence du masque)
1006 $r_ = $r + (($r2 - $r) * $v2 * (1 - $v));
1007 $g_ = $g + (($g2 - $g) * $v2 * (1 - $v));
1008 $b_ = $b + (($b2 - $b) * $v2 * (1 - $v));
1009 }
1010 }
1011 $a_ = $a2;
1012
1013 } else {
1014 $r_ = $r2 + 1 * ($r - 127);
1015 $r_ = max(0, min($r_, 255));
1016 $g_ = $g2 + 1 * ($g - 127);
1017 $g_ = max(0, min($g_, 255));
1018 $b_ = $b2 + 1 * ($b - 127);
1019 $b_ = max(0, min($b_, 255));
1020 $a_ = $a + $a2 - round($a * $a2 / 127);
1021 }
1022
1023 $color = ImageColorAllocateAlpha($im_, $r_, $g_, $b_, $a_);
1024 imagesetpixel($im_, $x, $y, $color);
1025 }
1026 }
1027
1028 _image_gd_output($im_, $image);
1029 imagedestroy($im_);
1030 imagedestroy($im);
1031 imagedestroy($im2);
1032
1033 }
1034 $x_dest = largeur($dest);
1035 $y_dest = hauteur($dest);
1036
1037 return _image_ecrire_tag($image, array('src' => $dest, 'width' => $x_dest, 'height' => $y_dest));
1038 }
1039
1040 // Passage de l'image en noir et blanc
1041 // un noir & blanc "photo" n'est pas "neutre": les composantes de couleur sont
1042 // ponderees pour obtenir le niveau de gris;
1043 // on peut ici regler cette ponderation en "pour mille"
1044 // https://code.spip.net/@image_nb
1045 function image_nb($im, $val_r = 299, $val_g = 587, $val_b = 114) {
1046 $fonction = array('image_nb', func_get_args());
1047 $image = _image_valeurs_trans($im, "nb-$val_r-$val_g-$val_b", false, $fonction);
1048 if (!$image) {
1049 return ("");
1050 }
1051
1052 $x_i = $image["largeur"];
1053 $y_i = $image["hauteur"];
1054
1055 $im = $image["fichier"];
1056 $dest = $image["fichier_dest"];
1057
1058 $creer = $image["creer"];
1059
1060 // Methode precise
1061 // resultat plus beau, mais tres lourd
1062 // Et: indispensable pour preserver transparence!
1063
1064 if ($creer) {
1065 // Creation de l'image en deux temps
1066 // de facon a conserver les GIF transparents
1067 $im = $image["fonction_imagecreatefrom"]($im);
1068 imagepalettetotruecolor($im);
1069 $im_ = imagecreatetruecolor($x_i, $y_i);
1070 @imagealphablending($im_, false);
1071 @imagesavealpha($im_, true);
1072 $color_t = ImageColorAllocateAlpha($im_, 255, 255, 255, 127);
1073 imagefill($im_, 0, 0, $color_t);
1074 imagecopy($im_, $im, 0, 0, 0, 0, $x_i, $y_i);
1075
1076 for ($x = 0; $x < $x_i; $x++) {
1077 for ($y = 0; $y < $y_i; $y++) {
1078 $rgb = ImageColorAt($im_, $x, $y);
1079 $a = ($rgb >> 24) & 0xFF;
1080 $r = ($rgb >> 16) & 0xFF;
1081 $g = ($rgb >> 8) & 0xFF;
1082 $b = $rgb & 0xFF;
1083
1084 $c = round(($val_r * $r / 1000) + ($val_g * $g / 1000) + ($val_b * $b / 1000));
1085 if ($c < 0) {
1086 $c = 0;
1087 }
1088 if ($c > 254) {
1089 $c = 254;
1090 }
1091
1092
1093 $color = ImageColorAllocateAlpha($im_, $c, $c, $c, $a);
1094 imagesetpixel($im_, $x, $y, $color);
1095 }
1096 }
1097 _image_gd_output($im_, $image);
1098 imagedestroy($im_);
1099 imagedestroy($im);
1100 }
1101
1102 return _image_ecrire_tag($image, array('src' => $dest));
1103 }
1104
1105 // https://code.spip.net/@image_flou
1106 function image_flou($im, $niveau = 3) {
1107 // Il s'agit d'une modification du script blur qu'on trouve un peu partout:
1108 // + la transparence est geree correctement
1109 // + les dimensions de l'image sont augmentees pour flouter les bords
1110 $coeffs = array(
1111 array(1),
1112 array(1, 1),
1113 array(1, 2, 1),
1114 array(1, 3, 3, 1),
1115 array(1, 4, 6, 4, 1),
1116 array(1, 5, 10, 10, 5, 1),
1117 array(1, 6, 15, 20, 15, 6, 1),
1118 array(1, 7, 21, 35, 35, 21, 7, 1),
1119 array(1, 8, 28, 56, 70, 56, 28, 8, 1),
1120 array(1, 9, 36, 84, 126, 126, 84, 36, 9, 1),
1121 array(1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1),
1122 array(1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1)
1123 );
1124
1125 $fonction = array('image_flou', func_get_args());
1126 $image = _image_valeurs_trans($im, "flou-$niveau", false, $fonction);
1127 if (!$image) {
1128 return ("");
1129 }
1130
1131 $x_i = $image["largeur"];
1132 $y_i = $image["hauteur"];
1133 $sum = pow(2, $niveau);
1134
1135 $im = $image["fichier"];
1136 $dest = $image["fichier_dest"];
1137
1138 $creer = $image["creer"];
1139
1140 // Methode precise
1141 // resultat plus beau, mais tres lourd
1142 // Et: indispensable pour preserver transparence!
1143
1144 if ($creer) {
1145 // Creation de l'image en deux temps
1146 // de facon a conserver les GIF transparents
1147 $im = $image["fonction_imagecreatefrom"]($im);
1148 imagepalettetotruecolor($im);
1149 $temp1 = imagecreatetruecolor($x_i + $niveau, $y_i);
1150 $temp2 = imagecreatetruecolor($x_i + $niveau, $y_i + $niveau);
1151
1152 @imagealphablending($temp1, false);
1153 @imagesavealpha($temp1, true);
1154 @imagealphablending($temp2, false);
1155 @imagesavealpha($temp2, true);
1156
1157
1158 for ($i = 0; $i < $x_i + $niveau; $i++) {
1159 for ($j = 0; $j < $y_i; $j++) {
1160 $suma = 0;
1161 $sumr = 0;
1162 $sumg = 0;
1163 $sumb = 0;
1164 $sum = 0;
1165 $sum_ = 0;
1166 for ($k = 0; $k <= $niveau; ++$k) {
1167 $color = imagecolorat($im, $i_ = ($i - $niveau) + $k, $j);
1168
1169 $a = ($color >> 24) & 0xFF;
1170 $r = ($color >> 16) & 0xFF;
1171 $g = ($color >> 8) & 0xFF;
1172 $b = ($color) & 0xFF;
1173
1174 if ($i_ < 0 or $i_ >= $x_i) {
1175 $a = 127;
1176 }
1177
1178 $coef = $coeffs[$niveau][$k];
1179 $suma += $a * $coef;
1180 $ac = ((127 - $a) / 127);
1181
1182 $ac = $ac * $ac;
1183
1184 $sumr += $r * $coef * $ac;
1185 $sumg += $g * $coef * $ac;
1186 $sumb += $b * $coef * $ac;
1187 $sum += $coef * $ac;
1188 $sum_ += $coef;
1189 }
1190 if ($sum > 0) {
1191 $color = ImageColorAllocateAlpha($temp1, $sumr / $sum, $sumg / $sum, $sumb / $sum, $suma / $sum_);
1192 } else {
1193 $color = ImageColorAllocateAlpha($temp1, 255, 255, 255, 127);
1194 }
1195 imagesetpixel($temp1, $i, $j, $color);
1196 }
1197 }
1198 imagedestroy($im);
1199 for ($i = 0; $i < $x_i + $niveau; $i++) {
1200 for ($j = 0; $j < $y_i + $niveau; $j++) {
1201 $suma = 0;
1202 $sumr = 0;
1203 $sumg = 0;
1204 $sumb = 0;
1205 $sum = 0;
1206 $sum_ = 0;
1207 for ($k = 0; $k <= $niveau; ++$k) {
1208 $color = imagecolorat($temp1, $i, $j_ = $j - $niveau + $k);
1209 $a = ($color >> 24) & 0xFF;
1210 $r = ($color >> 16) & 0xFF;
1211 $g = ($color >> 8) & 0xFF;
1212 $b = ($color) & 0xFF;
1213 if ($j_ < 0 or $j_ >= $y_i) {
1214 $a = 127;
1215 }
1216
1217 $suma += $a * $coeffs[$niveau][$k];
1218 $ac = ((127 - $a) / 127);
1219
1220 $sumr += $r * $coeffs[$niveau][$k] * $ac;
1221 $sumg += $g * $coeffs[$niveau][$k] * $ac;
1222 $sumb += $b * $coeffs[$niveau][$k] * $ac;
1223 $sum += $coeffs[$niveau][$k] * $ac;
1224 $sum_ += $coeffs[$niveau][$k];
1225
1226 }
1227 if ($sum > 0) {
1228 $color = ImageColorAllocateAlpha($temp2, $sumr / $sum, $sumg / $sum, $sumb / $sum, $suma / $sum_);
1229 } else {
1230 $color = ImageColorAllocateAlpha($temp2, 255, 255, 255, 127);
1231 }
1232 imagesetpixel($temp2, $i, $j, $color);
1233 }
1234 }
1235
1236 _image_gd_output($temp2, $image);
1237 imagedestroy($temp1);
1238 imagedestroy($temp2);
1239 }
1240
1241 return _image_ecrire_tag($image, array('src' => $dest, 'width' => ($x_i + $niveau), 'height' => ($y_i + $niveau)));
1242 }
1243
1244 // https://code.spip.net/@image_RotateBicubic
1245 function image_RotateBicubic($src_img, $angle, $bicubic = 0) {
1246 include_spip('filtres/images_lib');
1247
1248 if (round($angle / 90) * 90 == $angle) {
1249 $droit = true;
1250 if (round($angle / 180) * 180 == $angle) {
1251 $rot = 180;
1252 } else {
1253 $rot = 90;
1254 }
1255 } else {
1256 $droit = false;
1257 }
1258
1259 // convert degrees to radians
1260 $angle = $angle + 180;
1261 $angle = deg2rad($angle);
1262
1263
1264 $src_x = imagesx($src_img);
1265 $src_y = imagesy($src_img);
1266
1267
1268 $center_x = floor(($src_x - 1) / 2);
1269 $center_y = floor(($src_y - 1) / 2);
1270
1271 $cosangle = cos($angle);
1272 $sinangle = sin($angle);
1273
1274 // calculer dimensions en simplifiant angles droits, ce qui evite "floutage"
1275 // des rotations a angle droit
1276 if (!$droit) {
1277 $corners = array(array(0, 0), array($src_x, 0), array($src_x, $src_y), array(0, $src_y));
1278
1279 foreach ($corners as $key => $value) {
1280 $value[0] -= $center_x; //Translate coords to center for rotation
1281 $value[1] -= $center_y;
1282 $temp = array();
1283 $temp[0] = $value[0] * $cosangle + $value[1] * $sinangle;
1284 $temp[1] = $value[1] * $cosangle - $value[0] * $sinangle;
1285 $corners[$key] = $temp;
1286 }
1287
1288 $min_x = 1000000000000000;
1289 $max_x = -1000000000000000;
1290 $min_y = 1000000000000000;
1291 $max_y = -1000000000000000;
1292
1293 foreach ($corners as $key => $value) {
1294 if ($value[0] < $min_x) {
1295 $min_x = $value[0];
1296 }
1297 if ($value[0] > $max_x) {
1298 $max_x = $value[0];
1299 }
1300
1301 if ($value[1] < $min_y) {
1302 $min_y = $value[1];
1303 }
1304 if ($value[1] > $max_y) {
1305 $max_y = $value[1];
1306 }
1307 }
1308
1309 $rotate_width = ceil($max_x - $min_x);
1310 $rotate_height = ceil($max_y - $min_y);
1311 } else {
1312 if ($rot == 180) {
1313 $rotate_height = $src_y;
1314 $rotate_width = $src_x;
1315 } else {
1316 $rotate_height = $src_x;
1317 $rotate_width = $src_y;
1318 }
1319 $bicubic = false;
1320 }
1321
1322
1323 $rotate = imagecreatetruecolor($rotate_width, $rotate_height);
1324 imagealphablending($rotate, false);
1325 imagesavealpha($rotate, true);
1326
1327 $cosangle = cos($angle);
1328 $sinangle = sin($angle);
1329
1330 // arrondir pour rotations angle droit (car cos et sin dans {-1,0,1})
1331 if ($droit) {
1332 $cosangle = round($cosangle);
1333 $sinangle = round($sinangle);
1334 }
1335
1336 $newcenter_x = ($rotate_width - 1) / 2;
1337 $newcenter_y = ($rotate_height - 1) / 2;
1338
1339
1340 for ($y = 0; $y < $rotate_height; $y++) {
1341 for ($x = 0; $x < $rotate_width; $x++) {
1342 // rotate...
1343 $old_x = ((($newcenter_x - $x) * $cosangle + ($newcenter_y - $y) * $sinangle))
1344 + $center_x;
1345 $old_y = ((($newcenter_y - $y) * $cosangle - ($newcenter_x - $x) * $sinangle))
1346 + $center_y;
1347
1348 $old_x = ceil($old_x);
1349 $old_y = ceil($old_y);
1350
1351 if ($old_x >= 0 && $old_x < $src_x
1352 && $old_y >= 0 && $old_y < $src_y
1353 ) {
1354 if ($bicubic == true) {
1355 $xo = $old_x;
1356 $x0 = floor($xo);
1357 $x1 = ceil($xo);
1358 $yo = $old_y;
1359 $y0 = floor($yo);
1360 $y1 = ceil($yo);
1361
1362 // on prend chaque point, mais on pondere en fonction de la distance
1363 $rgb = ImageColorAt($src_img, $x0, $y0);
1364 $a1 = ($rgb >> 24) & 0xFF;
1365 $r1 = ($rgb >> 16) & 0xFF;
1366 $g1 = ($rgb >> 8) & 0xFF;
1367 $b1 = $rgb & 0xFF;
1368 $d1 = _image_distance_pixel($xo, $yo, $x0, $y0);
1369
1370 $rgb = ImageColorAt($src_img, $x1, $y0);
1371 $a2 = ($rgb >> 24) & 0xFF;
1372 $r2 = ($rgb >> 16) & 0xFF;
1373 $g2 = ($rgb >> 8) & 0xFF;
1374 $b2 = $rgb & 0xFF;
1375 $d2 = _image_distance_pixel($xo, $yo, $x1, $y0);
1376
1377 $rgb = ImageColorAt($src_img, $x0, $y1);
1378 $a3 = ($rgb >> 24) & 0xFF;
1379 $r3 = ($rgb >> 16) & 0xFF;
1380 $g3 = ($rgb >> 8) & 0xFF;
1381 $b3 = $rgb & 0xFF;
1382 $d3 = _image_distance_pixel($xo, $yo, $x0, $y1);
1383
1384 $rgb = ImageColorAt($src_img, $x1, $y1);
1385 $a4 = ($rgb >> 24) & 0xFF;
1386 $r4 = ($rgb >> 16) & 0xFF;
1387 $g4 = ($rgb >> 8) & 0xFF;
1388 $b4 = $rgb & 0xFF;
1389 $d4 = _image_distance_pixel($xo, $yo, $x1, $y1);
1390
1391 $ac1 = ((127 - $a1) / 127);
1392 $ac2 = ((127 - $a2) / 127);
1393 $ac3 = ((127 - $a3) / 127);
1394 $ac4 = ((127 - $a4) / 127);
1395
1396 // limiter impact des couleurs transparentes,
1397 // mais attention tout transp: division par 0
1398 if ($ac1 * $d1 + $ac2 * $d2 + $ac3 + $d3 + $ac4 + $d4 > 0) {
1399 if ($ac1 > 0) {
1400 $d1 = $d1 * $ac1;
1401 }
1402 if ($ac2 > 0) {
1403 $d2 = $d2 * $ac2;
1404 }
1405 if ($ac3 > 0) {
1406 $d3 = $d3 * $ac3;
1407 }
1408 if ($ac4 > 0) {
1409 $d4 = $d4 * $ac4;
1410 }
1411 }
1412
1413 $tot = $d1 + $d2 + $d3 + $d4;
1414
1415 $r = round((($d1 * $r1) + ($d2 * $r2) + ($d3 * $r3) + ($d4 * $r4)) / $tot);
1416 $g = round((($d1 * $g1 + ($d2 * $g2) + $d3 * $g3 + $d4 * $g4)) / $tot);
1417 $b = round((($d1 * $b1 + ($d2 * $b2) + $d3 * $b3 + $d4 * $b4)) / $tot);
1418 $a = round((($d1 * $a1 + ($d2 * $a2) + $d3 * $a3 + $d4 * $a4)) / $tot);
1419 $color = imagecolorallocatealpha($src_img, $r, $g, $b, $a);
1420 } else {
1421 $color = imagecolorat($src_img, round($old_x), round($old_y));
1422 }
1423 } else {
1424 // this line sets the background colour
1425 $color = imagecolorallocatealpha($src_img, 255, 255, 255, 127);
1426 }
1427 @imagesetpixel($rotate, $x, $y, $color);
1428 }
1429 }
1430
1431 return $rotate;
1432 }
1433
1434 // permet de faire tourner une image d'un angle quelconque
1435 // la fonction "crop" n'est pas implementee...
1436 // https://code.spip.net/@image_rotation
1437 function image_rotation($im, $angle, $crop = false) {
1438 $fonction = array('image_rotation', func_get_args());
1439 $image = _image_valeurs_trans($im, "rot-$angle-$crop", "png", $fonction);
1440 if (!$image) {
1441 return ("");
1442 }
1443
1444 $im = $image["fichier"];
1445 $dest = $image["fichier_dest"];
1446
1447 $creer = $image["creer"];
1448
1449 if ($creer) {
1450 $effectuer_gd = true;
1451
1452 if (method_exists('Imagick', 'rotateImage')) {
1453 $imagick = new Imagick();
1454 $imagick->readImage($im);
1455 $imagick->rotateImage(new ImagickPixel('none'), $angle);
1456 $imagick->writeImage($dest);
1457 $effectuer_gd = false;
1458 } else {
1459 if ($GLOBALS['meta']['image_process'] == "convert") {
1460 if (_CONVERT_COMMAND != '') {
1461 @define('_CONVERT_COMMAND', 'convert');
1462 @define('_ROTATE_COMMAND', _CONVERT_COMMAND . ' -background none -rotate %t %src %dest');
1463 } else {
1464 @define('_ROTATE_COMMAND', '');
1465 }
1466 if (_ROTATE_COMMAND !== '') {
1467 $commande = str_replace(
1468 array('%t', '%src', '%dest'),
1469 array(
1470 $angle,
1471 escapeshellcmd($im),
1472 escapeshellcmd($dest)
1473 ),
1474 _ROTATE_COMMAND);
1475 spip_log($commande);
1476 exec($commande);
1477 if (file_exists($dest)) // precaution
1478 {
1479 $effectuer_gd = false;
1480 }
1481 }
1482 }
1483 // cette variante genere-t-elle un fond transparent
1484 // dans les coins vide issus de la rotation ?
1485 elseif (function_exists('imagick_rotate')) {
1486 $handle = imagick_readimage($im);
1487 if ($handle && imagick_isopaqueimage($handle)) {
1488 imagick_setfillcolor($handle, 'transparent');
1489 imagick_rotate($handle, $angle);
1490 imagick_writeimage($handle, $dest);
1491 $effectuer_gd = false;
1492 }
1493 }
1494 }
1495 if ($effectuer_gd) {
1496 // Creation de l'image en deux temps
1497 // de facon a conserver les GIF transparents
1498 $im = $image["fonction_imagecreatefrom"]($im);
1499 imagepalettetotruecolor($im);
1500 $im = image_RotateBicubic($im, $angle, true);
1501 _image_gd_output($im, $image);
1502 imagedestroy($im);
1503 }
1504 }
1505 list($src_y, $src_x) = taille_image($dest);
1506
1507 return _image_ecrire_tag($image, array('src' => $dest, 'width' => $src_x, 'height' => $src_y));
1508 }
1509
1510 // Permet d'appliquer un filtre php_imagick a une image
1511 // par exemple: [(#LOGO_ARTICLE|image_imagick{imagick_wave,20,60})]
1512 // liste des fonctions: http://www.linux-nantes.org/~fmonnier/doc/imagick/
1513 // https://code.spip.net/@image_imagick
1514 function image_imagick() {
1515 $tous = func_get_args();
1516 $img = $tous[0];
1517 $fonc = $tous[1];
1518 $tous[0] = "";
1519 $tous_var = join($tous, "-");
1520
1521 $fonction = array('image_imagick', func_get_args());
1522 $image = _image_valeurs_trans($img, "$tous_var", "png", $fonction);
1523 if (!$image) {
1524 return ("");
1525 }
1526
1527 $im = $image["fichier"];
1528 $dest = $image["fichier_dest"];
1529
1530 $creer = $image["creer"];
1531
1532 if ($creer) {
1533 if (function_exists($fonc)) {
1534
1535 $handle = imagick_readimage($im);
1536 $arr[0] = $handle;
1537 for ($i = 2; $i < count($tous); $i++) {
1538 $arr[] = $tous[$i];
1539 }
1540 call_user_func_array($fonc, $arr);
1541 // Creer image dans fichier temporaire, puis renommer vers "bon" fichier
1542 // de facon a eviter time_out pendant creation de l'image definitive
1543 $tmp = preg_replace(",[.]png$,i", "-tmp.png", $dest);
1544 imagick_writeimage($handle, $tmp);
1545 rename($tmp, $dest);
1546 ecrire_fichier($dest . ".src", serialize($image));
1547 }
1548 }
1549 list($src_y, $src_x) = taille_image($dest);
1550
1551 return _image_ecrire_tag($image, array('src' => $dest, 'width' => $src_x, 'height' => $src_y));
1552
1553 }
1554
1555 // Permet de rendre une image
1556 // plus claire (gamma > 0)
1557 // ou plus foncee (gamma < 0)
1558 // https://code.spip.net/@image_gamma
1559 function image_gamma($im, $gamma = 0) {
1560 include_spip('filtres/images_lib');
1561 $fonction = array('image_gamma', func_get_args());
1562 $image = _image_valeurs_trans($im, "gamma-$gamma", false, $fonction);
1563 if (!$image) {
1564 return ("");
1565 }
1566
1567 $x_i = $image["largeur"];
1568 $y_i = $image["hauteur"];
1569
1570 $im = $image["fichier"];
1571 $dest = $image["fichier_dest"];
1572
1573 $creer = $image["creer"];
1574
1575 if ($creer) {
1576 // Creation de l'image en deux temps
1577 // de facon a conserver les GIF transparents
1578 $im = $image["fonction_imagecreatefrom"]($im);
1579 imagepalettetotruecolor($im);
1580 $im_ = imagecreatetruecolor($x_i, $y_i);
1581 @imagealphablending($im_, false);
1582 @imagesavealpha($im_, true);
1583 $color_t = ImageColorAllocateAlpha($im_, 255, 255, 255, 127);
1584 imagefill($im_, 0, 0, $color_t);
1585 imagecopy($im_, $im, 0, 0, 0, 0, $x_i, $y_i);
1586
1587 for ($x = 0; $x < $x_i; $x++) {
1588 for ($y = 0; $y < $y_i; $y++) {
1589 $rgb = ImageColorAt($im_, $x, $y);
1590 $a = ($rgb >> 24) & 0xFF;
1591 $r = ($rgb >> 16) & 0xFF;
1592 $g = ($rgb >> 8) & 0xFF;
1593 $b = $rgb & 0xFF;
1594
1595 $r = _image_decale_composante($r, $gamma);
1596 $g = _image_decale_composante($g, $gamma);
1597 $b = _image_decale_composante($b, $gamma);
1598
1599 $color = ImageColorAllocateAlpha($im_, $r, $g, $b, $a);
1600 imagesetpixel($im_, $x, $y, $color);
1601 }
1602 }
1603 _image_gd_output($im_, $image);
1604 }
1605
1606 return _image_ecrire_tag($image, array('src' => $dest));
1607 }
1608
1609 // Passe l'image en "sepia"
1610 // On peut fixer les valeurs RGB
1611 // de la couleur "complementaire" pour forcer une dominante
1612 //function image_sepia($im, $dr = 137, $dv = 111, $db = 94)
1613 // https://code.spip.net/@image_sepia
1614 function image_sepia($im, $rgb = "896f5e") {
1615 include_spip('filtres/images_lib');
1616
1617 if (!function_exists("imagecreatetruecolor")) {
1618 return $im;
1619 }
1620
1621 $couleurs = _couleur_hex_to_dec($rgb);
1622 $dr = $couleurs["red"];
1623 $dv = $couleurs["green"];
1624 $db = $couleurs["blue"];
1625
1626 $fonction = array('image_sepia', func_get_args());
1627 $image = _image_valeurs_trans($im, "sepia-$dr-$dv-$db", false, $fonction);
1628 if (!$image) {
1629 return ("");
1630 }
1631
1632 $x_i = $image["largeur"];
1633 $y_i = $image["hauteur"];
1634
1635 $im = $image["fichier"];
1636 $dest = $image["fichier_dest"];
1637
1638 $creer = $image["creer"];
1639
1640 if ($creer) {
1641 // Creation de l'image en deux temps
1642 // de facon a conserver les GIF transparents
1643 $im = $image["fonction_imagecreatefrom"]($im);
1644 imagepalettetotruecolor($im);
1645 $im_ = imagecreatetruecolor($x_i, $y_i);
1646 @imagealphablending($im_, false);
1647 @imagesavealpha($im_, true);
1648 $color_t = ImageColorAllocateAlpha($im_, 255, 255, 255, 127);
1649 imagefill($im_, 0, 0, $color_t);
1650 imagecopy($im_, $im, 0, 0, 0, 0, $x_i, $y_i);
1651
1652 for ($x = 0; $x < $x_i; $x++) {
1653 for ($y = 0; $y < $y_i; $y++) {
1654 $rgb = ImageColorAt($im_, $x, $y);
1655 $a = ($rgb >> 24) & 0xFF;
1656 $r = ($rgb >> 16) & 0xFF;
1657 $g = ($rgb >> 8) & 0xFF;
1658 $b = $rgb & 0xFF;
1659
1660 $r = round(.299 * $r + .587 * $g + .114 * $b);
1661 $g = $r;
1662 $b = $r;
1663
1664
1665 $r = _image_decale_composante_127($r, $dr);
1666 $g = _image_decale_composante_127($g, $dv);
1667 $b = _image_decale_composante_127($b, $db);
1668
1669 $color = ImageColorAllocateAlpha($im_, $r, $g, $b, $a);
1670 imagesetpixel($im_, $x, $y, $color);
1671 }
1672 }
1673 _image_gd_output($im_, $image);
1674 imagedestroy($im_);
1675 imagedestroy($im);
1676 }
1677
1678 return _image_ecrire_tag($image, array('src' => $dest));
1679 }
1680
1681
1682 /**
1683 * Renforcer la netteté d'une image
1684 *
1685 * @param string $im
1686 * Code HTML de l'image
1687 * @param float $k
1688 * Niveau de renforcement (entre 0 et 1)
1689 * @return string Code HTML de l'image
1690 **/
1691 function image_renforcement($im, $k = 0.5) {
1692 $fonction = array('image_flou', func_get_args());
1693 $image = _image_valeurs_trans($im, "renforcement-$k", false, $fonction);
1694 if (!$image) {
1695 return ("");
1696 }
1697
1698 $x_i = $image["largeur"];
1699 $y_i = $image["hauteur"];
1700 $im = $image["fichier"];
1701 $dest = $image["fichier_dest"];
1702 $creer = $image["creer"];
1703
1704 if ($creer) {
1705 $im = $image["fonction_imagecreatefrom"]($im);
1706 imagepalettetotruecolor($im);
1707 $im_ = imagecreatetruecolor($x_i, $y_i);
1708 @imagealphablending($im_, false);
1709 @imagesavealpha($im_, true);
1710 $color_t = ImageColorAllocateAlpha($im_, 255, 255, 255, 127);
1711 imagefill($im_, 0, 0, $color_t);
1712
1713 for ($x = 0; $x < $x_i; $x++) {
1714 for ($y = 0; $y < $y_i; $y++) {
1715
1716 $rgb[1][0] = @imagecolorat($im, $x, $y - 1);
1717 $rgb[0][1] = @imagecolorat($im, $x - 1, $y);
1718 $rgb[1][1] = @imagecolorat($im, $x, $y);
1719 $rgb[2][1] = @imagecolorat($im, $x + 1, $y);
1720 $rgb[1][2] = @imagecolorat($im, $x, $y + 1);
1721
1722 if ($x - 1 < 0) {
1723 $rgb[0][1] = $rgb[1][1];
1724 }
1725 if ($y - 1 < 0) {
1726 $rgb[1][0] = $rgb[1][1];
1727 }
1728 if ($x + 1 == $x_i) {
1729 $rgb[2][1] = $rgb[1][1];
1730 }
1731 if ($y + 1 == $y_i) {
1732 $rgb[1][2] = $rgb[1][1];
1733 }
1734
1735 $a = ($rgb[1][1] >> 24) & 0xFF;
1736 $r = -$k * (($rgb[1][0] >> 16) & 0xFF) +
1737 -$k * (($rgb[0][1] >> 16) & 0xFF) +
1738 (1 + 4 * $k) * (($rgb[1][1] >> 16) & 0xFF) +
1739 -$k * (($rgb[2][1] >> 16) & 0xFF) +
1740 -$k * (($rgb[1][2] >> 16) & 0xFF);
1741
1742 $g = -$k * (($rgb[1][0] >> 8) & 0xFF) +
1743 -$k * (($rgb[0][1] >> 8) & 0xFF) +
1744 (1 + 4 * $k) * (($rgb[1][1] >> 8) & 0xFF) +
1745 -$k * (($rgb[2][1] >> 8) & 0xFF) +
1746 -$k * (($rgb[1][2] >> 8) & 0xFF);
1747
1748 $b = -$k * ($rgb[1][0] & 0xFF) +
1749 -$k * ($rgb[0][1] & 0xFF) +
1750 (1 + 4 * $k) * ($rgb[1][1] & 0xFF) +
1751 -$k * ($rgb[2][1] & 0xFF) +
1752 -$k * ($rgb[1][2] & 0xFF);
1753
1754 $r = min(255, max(0, $r));
1755 $g = min(255, max(0, $g));
1756 $b = min(255, max(0, $b));
1757
1758
1759 $color = ImageColorAllocateAlpha($im_, $r, $g, $b, $a);
1760 imagesetpixel($im_, $x, $y, $color);
1761 }
1762 }
1763 _image_gd_output($im_, $image);
1764 }
1765
1766 return _image_ecrire_tag($image, array('src' => $dest));
1767 }
1768
1769
1770 /**
1771 * Transforme la couleur de fond de l'image en transparence
1772 * Le filtre ne gere pas la notion de contiguite aux bords, et affectera tous les pixels de l'image dans la couleur visee
1773 * $background_color : couleur cible
1774 * $tolerance : distance L1 dans l'espace RGB des couleur autour de la couleur $background_color pour lequel la transparence sera appliquee
1775 * $alpha : transparence a appliquer pour les pixels de la couleur cibles avec la tolerance ci-dessus
1776 * $coeff_lissage : coeff applique a la tolerance pour determiner la decroissance de la transparence fonction de la distance L1 entre la couleur du pixel et la couleur cible
1777 *
1778 * @param string $im
1779 * @param string $background_color
1780 * @param int $tolerance
1781 * @param int $alpha
1782 * alpha = 0: aucune transparence
1783 * alpha = 127: completement transparent
1784 * @param int $coeff_lissage
1785 * @return mixed|null|string
1786 */
1787 function image_fond_transparent($im, $background_color, $tolerance = 12, $alpha = 127, $coeff_lissage = 7) {
1788 $fonction = array('image_fond_transparent', func_get_args());
1789 $image = _image_valeurs_trans($im, "fond_transparent-$background_color-$tolerance-$coeff_lissage-$alpha", "png",
1790 $fonction);
1791 if (!$image) {
1792 return ("");
1793 }
1794
1795 $x_i = $image["largeur"];
1796 $y_i = $image["hauteur"];
1797
1798 $im = $image["fichier"];
1799 $dest = $image["fichier_dest"];
1800
1801 $creer = $image["creer"];
1802
1803 if ($creer) {
1804 $bg = _couleur_hex_to_dec($background_color);
1805 $bg_r = $bg['red'];
1806 $bg_g = $bg['green'];
1807 $bg_b = $bg['blue'];
1808
1809 // Creation de l'image en deux temps
1810 // de facon a conserver les GIF transparents
1811 $im = $image["fonction_imagecreatefrom"]($im);
1812 imagepalettetotruecolor($im);
1813 $im2 = imagecreatetruecolor($x_i, $y_i);
1814 @imagealphablending($im2, false);
1815 @imagesavealpha($im2, true);
1816 $color_t = ImageColorAllocateAlpha($im2, 255, 255, 255, 127);
1817 imagefill($im2, 0, 0, $color_t);
1818 imagecopy($im2, $im, 0, 0, 0, 0, $x_i, $y_i);
1819
1820 $im_ = imagecreatetruecolor($x_i, $y_i);
1821 imagealphablending($im_, false);
1822 imagesavealpha($im_, true);
1823 $color_f = ImageColorAllocateAlpha($im_, 255, 255, 255, $alpha);
1824
1825 for ($x = 0; $x < $x_i; $x++) {
1826 for ($y = 0; $y < $y_i; $y++) {
1827 $rgb = ImageColorAt($im2, $x, $y);
1828 $r = ($rgb >> 16) & 0xFF;
1829 $g = ($rgb >> 8) & 0xFF;
1830 $b = $rgb & 0xFF;
1831 if ((($d = abs($r - $bg_r) + abs($g - $bg_g) + abs($b - $bg_b)) <= $tolerance)) {
1832 imagesetpixel($im_, $x, $y, $color_f);
1833 } elseif ($tolerance and $d <= ($coeff_lissage + 1) * $tolerance) {
1834 $transp = round($alpha * (1 - ($d - $tolerance) / ($coeff_lissage * $tolerance)));
1835 $color_p = ImageColorAllocateAlpha($im_, $r, $g, $b, $transp);
1836 imagesetpixel($im_, $x, $y, $color_p);
1837 } else {
1838 imagesetpixel($im_, $x, $y, $rgb);
1839 }
1840 }
1841 }
1842 _image_gd_output($im_, $image);
1843 imagedestroy($im_);
1844 imagedestroy($im);
1845 imagedestroy($im2);
1846 }
1847
1848 return _image_ecrire_tag($image, array('src' => $dest));
1849 }