function couleur_hsv2rgb($H, $S, $V)
{
    include_spip('filtres/images_lib');
    return _couleur_hsv2rgb($H, $S, $V);
}
function image_masque($im, $masque, $pos = "")
{
    // Passer, en plus de l'image d'origine,
    // une image de "masque": un fichier PNG24 transparent.
    // Le decoupage se fera selon la transparence du "masque",
    // et les couleurs seront eclaircies/foncees selon de couleur du masque.
    // Pour ne pas modifier la couleur, le masque doit etre en gris 50%.
    //
    // Si l'image source est plus grande que le masque, alors cette image est reduite a la taille du masque.
    // Sinon, c'est la taille de l'image source qui est utilisee.
    //
    // $pos est une variable libre, qui permet de passer left=..., right=..., bottom=..., top=...
    // dans ce cas, le masque est place a ces positions sur l'image d'origine,
    // et evidemment cette image d'origine n'est pas redimensionnee
    //
    // Positionnement horizontal: text-align=left, right, center
    // Positionnement vertical : vertical-align=top, bottom, middle
    // (les positionnements left, right, top, left sont relativement inutiles, mais coherence avec CSS)
    //
    // Choix du mode de fusion: mode=masque, normal, eclaircir, obscurcir, produit, difference, ecran, superposer, lumiere_dure, teinte, saturation, valeur
    // https://en.wikipedia.org/wiki/Blend_modes
    // masque: mode par defaut
    // normal: place la nouvelle image par dessus l'ancienne
    // eclaircir: place uniquement les points plus clairs
    // obscurcir: place uniquement les points plus fonc'es
    // produit: multiplie par le masque (points noirs rendent l'image noire, points blancs ne changent rien)
    // difference: remplit avec l'ecart entre les couleurs d'origine et du masque
    // ecran: effet inverse de 'produit' -> l'image resultante est plus claire
    // superposer: combine les modes 'produit' et 'ecran' -> les parties claires sont eclaircies, les parties sombres assombries.
    // lumiere_dure: equivalent a 'superposer', sauf que l'image du bas et du haut sont inversees.
    // teinte: utilise la teinte du masque
    // saturation: utilise la saturation du masque
    // valeur: utilise la valeur du masque
    $mode = "masque";
    $numargs = func_num_args();
    $arg_list = func_get_args();
    $variable = array();
    $texte = $arg_list[0];
    for ($i = 1; $i < $numargs; $i++) {
        if (($p = strpos($arg_list[$i], "=")) !== false) {
            $nom_variable = substr($arg_list[$i], 0, $p);
            $val_variable = substr($arg_list[$i], $p + 1);
            $variable["{$nom_variable}"] = $val_variable;
            $defini["{$nom_variable}"] = 1;
        }
    }
    if (isset($defini["mode"]) and $defini["mode"]) {
        $mode = $variable["mode"];
    }
    // utiliser _image_valeurs_trans pour accepter comme masque :
    // - une balise <img src='...' />
    // - une image avec un timestamp ?01234
    $mask = _image_valeurs_trans($masque, "source-image_masque", "png", null, true);
    if (!$mask) {
        return "";
    }
    $masque = $mask['fichier'];
    $pos = md5(serialize($variable) . $mask['date_src']);
    $fonction = array('image_masque', func_get_args());
    $image = _image_valeurs_trans($im, "masque-{$masque}-{$pos}", "png", $fonction);
    if (!$image) {
        return "";
    }
    $x_i = $image["largeur"];
    $y_i = $image["hauteur"];
    $im = $image["fichier"];
    $dest = $image["fichier_dest"];
    $creer = $image["creer"];
    // doit-on positionner l'image ?
    $placer = false;
    foreach (array("right", "left", "bottom", "top", "text-align", "vertical-align") as $pl) {
        if (isset($defini[$pl]) and $defini[$pl]) {
            $placer = true;
            break;
        }
    }
    if ($creer) {
        $im_m = $mask["fichier"];
        $x_m = $mask["largeur"];
        $y_m = $mask["hauteur"];
        $im2 = $mask["fonction_imagecreatefrom"]($masque);
        if ($mask["format_source"] == "gif" and function_exists('ImageCopyResampled')) {
            $im2_ = imagecreatetruecolor($x_m, $y_m);
            // Si un GIF est transparent,
            // fabriquer un PNG transparent
            // Conserver la transparence
            if (function_exists("imageAntiAlias")) {
                imageAntiAlias($im2_, true);
            }
            @imagealphablending($im2_, false);
            @imagesavealpha($im2_, true);
            @ImageCopyResampled($im2_, $im2, 0, 0, 0, 0, $x_m, $y_m, $x_m, $y_m);
            imagedestroy($im2);
            $im2 = $im2_;
        }
        if ($placer) {
            // On fabriquer une version "agrandie" du masque,
            // aux dimensions de l'image source
            // et on "installe" le masque dans cette image
            // ainsi: aucun redimensionnement
            $dx = 0;
            $dy = 0;
            if (isset($defini["right"]) and $defini["right"]) {
                $right = $variable["right"];
                $dx = $x_i - $x_m - $right;
            }
            if (isset($defini["bottom"]) and $defini["bottom"]) {
                $bottom = $variable["bottom"];
                $dy = $y_i - $y_m - $bottom;
            }
            if (isset($defini["top"]) and $defini["top"]) {
                $top = $variable["top"];
                $dy = $top;
            }
            if (isset($defini["left"]) and $defini["left"]) {
                $left = $variable["left"];
                $dx = $left;
            }
            if (isset($defini["text-align"]) and $defini["text-align"]) {
                $align = $variable["text-align"];
                if ($align == "right") {
                    $right = 0;
                    $dx = $x_i - $x_m;
                } else {
                    if ($align == "left") {
                        $left = 0;
                        $dx = 0;
                    } else {
                        if ($align = "center") {
                            $dx = round(($x_i - $x_m) / 2);
                        }
                    }
                }
            }
            if (isset($defini["vertical-align"]) and $defini["vertical-align"]) {
                $valign = $variable["vertical-align"];
                if ($valign == "bottom") {
                    $bottom = 0;
                    $dy = $y_i - $y_m;
                } else {
                    if ($valign == "top") {
                        $top = 0;
                        $dy = 0;
                    } else {
                        if ($valign = "middle") {
                            $dy = round(($y_i - $y_m) / 2);
                        }
                    }
                }
            }
            $im3 = imagecreatetruecolor($x_i, $y_i);
            @imagealphablending($im3, false);
            @imagesavealpha($im3, true);
            if ($mode == "masque") {
                $color_t = ImageColorAllocateAlpha($im3, 128, 128, 128, 0);
            } else {
                $color_t = ImageColorAllocateAlpha($im3, 128, 128, 128, 127);
            }
            imagefill($im3, 0, 0, $color_t);
            imagecopy($im3, $im2, $dx, $dy, 0, 0, $x_m, $y_m);
            imagedestroy($im2);
            $im2 = imagecreatetruecolor($x_i, $y_i);
            @imagealphablending($im2, false);
            @imagesavealpha($im2, true);
            imagecopy($im2, $im3, 0, 0, 0, 0, $x_i, $y_i);
            imagedestroy($im3);
            $x_m = $x_i;
            $y_m = $y_i;
        }
        $rapport = $x_i / $x_m;
        if ($y_i / $y_m < $rapport) {
            $rapport = $y_i / $y_m;
        }
        $x_d = ceil($x_i / $rapport);
        $y_d = ceil($y_i / $rapport);
        if ($x_i < $x_m or $y_i < $y_m) {
            $x_dest = $x_i;
            $y_dest = $y_i;
            $x_dec = 0;
            $y_dec = 0;
        } else {
            $x_dest = $x_m;
            $y_dest = $y_m;
            $x_dec = round(($x_d - $x_m) / 2);
            $y_dec = round(($y_d - $y_m) / 2);
        }
        $nouveau = _image_valeurs_trans(image_reduire($im, $x_d, $y_d), "");
        if (!is_array($nouveau)) {
            return "";
        }
        $im_n = $nouveau["fichier"];
        $im = $nouveau["fonction_imagecreatefrom"]($im_n);
        imagepalettetotruecolor($im);
        if ($nouveau["format_source"] == "gif" and function_exists('ImageCopyResampled')) {
            $im_ = imagecreatetruecolor($x_dest, $y_dest);
            // Si un GIF est transparent,
            // fabriquer un PNG transparent
            // Conserver la transparence
            if (function_exists("imageAntiAlias")) {
                imageAntiAlias($im_, true);
            }
            @imagealphablending($im_, false);
            @imagesavealpha($im_, true);
            @ImageCopyResampled($im_, $im, 0, 0, 0, 0, $x_dest, $y_dest, $x_dest, $y_dest);
            imagedestroy($im);
            $im = $im_;
        }
        $im_ = imagecreatetruecolor($x_dest, $y_dest);
        @imagealphablending($im_, false);
        @imagesavealpha($im_, true);
        $color_t = ImageColorAllocateAlpha($im_, 255, 255, 255, 127);
        imagefill($im_, 0, 0, $color_t);
        // calcul couleurs de chaque pixel selon les modes de fusion
        for ($x = 0; $x < $x_dest; $x++) {
            for ($y = 0; $y < $y_dest; $y++) {
                $rgb = ImageColorAt($im2, $x, $y);
                // image au dessus
                $a = $rgb >> 24 & 0xff;
                $r = $rgb >> 16 & 0xff;
                $g = $rgb >> 8 & 0xff;
                $b = $rgb & 0xff;
                $rgb2 = ImageColorAt($im, $x + $x_dec, $y + $y_dec);
                // image en dessous
                $a2 = $rgb2 >> 24 & 0xff;
                $r2 = $rgb2 >> 16 & 0xff;
                $g2 = $rgb2 >> 8 & 0xff;
                $b2 = $rgb2 & 0xff;
                if ($mode == "normal") {
                    $v = (127 - $a) / 127;
                    if ($v == 1) {
                        $r_ = $r;
                        $g_ = $g;
                        $b_ = $b;
                    } else {
                        $v2 = (127 - $a2) / 127;
                        if ($v + $v2 == 0) {
                            $r_ = $r2;
                            $g_ = $g2;
                            $b_ = $b2;
                        } else {
                            if ($v2 == 0) {
                                $r_ = $r;
                                $g_ = $g;
                                $b_ = $b;
                            } else {
                                if ($v == 0) {
                                    $r_ = $r2;
                                    $g_ = $g2;
                                    $b_ = $b2;
                                } else {
                                    $r_ = $r + ($r2 - $r) * $v2 * (1 - $v);
                                    $g_ = $g + ($g2 - $g) * $v2 * (1 - $v);
                                    $b_ = $b + ($b2 - $b) * $v2 * (1 - $v);
                                }
                            }
                        }
                    }
                    $a_ = min($a, $a2);
                } elseif (in_array($mode, array("produit", "difference", "superposer", "lumiere_dure", "ecran"))) {
                    if ($mode == "produit") {
                        $r = $r / 255 * $r2;
                        $g = $g / 255 * $g2;
                        $b = $b / 255 * $b2;
                    } elseif ($mode == "difference") {
                        $r = abs($r - $r2);
                        $g = abs($g - $g2);
                        $b = abs($b - $b2);
                    } elseif ($mode == "superposer") {
                        $r = $r2 < 128 ? 2 * $r * $r2 / 255 : 255 - 2 * (255 - $r) * (255 - $r2) / 255;
                        $g = $g2 < 128 ? 2 * $g * $g2 / 255 : 255 - 2 * (255 - $g) * (255 - $g2) / 255;
                        $b = $b2 < 128 ? 2 * $b * $b2 / 255 : 255 - 2 * (255 - $b) * (255 - $b2) / 255;
                    } elseif ($mode == "lumiere_dure") {
                        $r = $r < 128 ? 2 * $r * $r2 / 255 : 255 - 2 * (255 - $r2) * (255 - $r) / 255;
                        $g = $g < 128 ? 2 * $g * $g2 / 255 : 255 - 2 * (255 - $g2) * (255 - $g) / 255;
                        $b = $b < 128 ? 2 * $b * $b2 / 255 : 255 - 2 * (255 - $b2) * (255 - $b) / 255;
                    } elseif ($mode == "ecran") {
                        $r = 255 - (255 - $r) * (255 - $r2) / 255;
                        $g = 255 - (255 - $g) * (255 - $g2) / 255;
                        $b = 255 - (255 - $b) * (255 - $b2) / 255;
                    }
                    $r = max(0, min($r, 255));
                    $g = max(0, min($g, 255));
                    $b = max(0, min($b, 255));
                    // melange en fonction de la transparence du masque
                    $v = (127 - $a) / 127;
                    if ($v == 1) {
                        // melange complet
                        $r_ = $r;
                        $g_ = $g;
                        $b_ = $b;
                    } else {
                        $v2 = (127 - $a2) / 127;
                        if ($v + $v2 == 0) {
                            // ??
                            $r_ = $r2;
                            $g_ = $g2;
                            $b_ = $b2;
                        } else {
                            // pas de melange (transparence du masque)
                            $r_ = $r + ($r2 - $r) * $v2 * (1 - $v);
                            $g_ = $g + ($g2 - $g) * $v2 * (1 - $v);
                            $b_ = $b + ($b2 - $b) * $v2 * (1 - $v);
                        }
                    }
                    $a_ = $a2;
                } elseif ($mode == "eclaircir" or $mode == "obscurcir") {
                    $v = (127 - $a) / 127;
                    if ($v == 1) {
                        $r_ = $r;
                        $g_ = $g;
                        $b_ = $b;
                    } else {
                        $v2 = (127 - $a2) / 127;
                        if ($v + $v2 == 0) {
                            $r_ = $r2;
                            $g_ = $g2;
                            $b_ = $b2;
                        } else {
                            $r_ = $r + ($r2 - $r) * $v2 * (1 - $v);
                            $g_ = $g + ($g2 - $g) * $v2 * (1 - $v);
                            $b_ = $b + ($b2 - $b) * $v2 * (1 - $v);
                        }
                    }
                    if ($mode == "eclaircir") {
                        $r_ = max($r_, $r2);
                        $g_ = max($g_, $g2);
                        $b_ = max($b_, $b2);
                    } else {
                        $r_ = min($r_, $r2);
                        $g_ = min($g_, $g2);
                        $b_ = min($b_, $b2);
                    }
                    $a_ = min($a, $a2);
                } elseif (in_array($mode, array("teinte", "saturation", "valeur"))) {
                    include_spip("filtres/images_lib");
                    $hsv = _couleur_rgb2hsv($r, $g, $b);
                    // image au dessus
                    $h = $hsv["h"];
                    $s = $hsv["s"];
                    $v = $hsv["v"];
                    $hsv2 = _couleur_rgb2hsv($r2, $g2, $b2);
                    // image en dessous
                    $h2 = $hsv2["h"];
                    $s2 = $hsv2["s"];
                    $v2 = $hsv2["v"];
                    switch ($mode) {
                        case "teinte":
                            $rgb3 = _couleur_hsv2rgb($h, $s2, $v2);
                            break;
                        case "saturation":
                            $rgb3 = _couleur_hsv2rgb($h2, $s, $v2);
                            break;
                        case "valeur":
                            $rgb3 = _couleur_hsv2rgb($h2, $s2, $v);
                            break;
                    }
                    $r = $rgb3["r"];
                    $g = $rgb3["g"];
                    $b = $rgb3["b"];
                    // melange en fonction de la transparence du masque
                    $v = (127 - $a) / 127;
                    if ($v == 1) {
                        // melange complet
                        $r_ = $r;
                        $g_ = $g;
                        $b_ = $b;
                    } else {
                        $v2 = (127 - $a2) / 127;
                        if ($v + $v2 == 0) {
                            // ??
                            $r_ = $r2;
                            $g_ = $g2;
                            $b_ = $b2;
                        } else {
                            // pas de melange (transparence du masque)
                            $r_ = $r + ($r2 - $r) * $v2 * (1 - $v);
                            $g_ = $g + ($g2 - $g) * $v2 * (1 - $v);
                            $b_ = $b + ($b2 - $b) * $v2 * (1 - $v);
                        }
                    }
                    $a_ = $a2;
                } else {
                    $r_ = $r2 + 1 * ($r - 127);
                    $r_ = max(0, min($r_, 255));
                    $g_ = $g2 + 1 * ($g - 127);
                    $g_ = max(0, min($g_, 255));
                    $b_ = $b2 + 1 * ($b - 127);
                    $b_ = max(0, min($b_, 255));
                    $a_ = $a + $a2 - round($a * $a2 / 127);
                }
                $color = ImageColorAllocateAlpha($im_, $r_, $g_, $b_, $a_);
                imagesetpixel($im_, $x, $y, $color);
            }
        }
        _image_gd_output($im_, $image);
        imagedestroy($im_);
        imagedestroy($im);
        imagedestroy($im2);
    }
    $x_dest = largeur($dest);
    $y_dest = hauteur($dest);
    return _image_ecrire_tag($image, array('src' => $dest, 'width' => $x_dest, 'height' => $y_dest));
}