La transparence des images PNG peut-elle être préservée lors de l'utilisation de l'image GDlib de PHP rééchantillonnée?

101

L'extrait de code PHP suivant utilise GD pour redimensionner un PNG téléchargé par navigateur en 128x128. Cela fonctionne très bien, sauf que les zones transparentes de l'image d'origine sont remplacées par une couleur unie - noir dans mon cas.

Même si imagesavealphac'est réglé, quelque chose ne va pas.

Quelle est la meilleure façon de préserver la transparence de l'image rééchantillonnée?

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile );    
imagesavealpha( $targetImage, true );

$targetImage = imagecreatetruecolor( 128, 128 );
imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );
Cheekysoft
la source

Réponses:

200
imagealphablending( $targetImage, false );
imagesavealpha( $targetImage, true );

l'a fait pour moi. Merci ceejayoz.

notez que l'image cible a besoin des paramètres alpha, pas de l'image source.

Edit: code de remplacement complet. Voir également les réponses ci-dessous et leurs commentaires. Ce n'est pas garanti d'être parfait en aucune façon, mais cela a répondu à mes besoins à l'époque.

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile ); 

$targetImage = imagecreatetruecolor( 128, 128 );   
imagealphablending( $targetImage, false );
imagesavealpha( $targetImage, true );

imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );
Cheekysoft
la source
5
FIY, cela doit être une fois que l'image cible a été créée. Dans ce cas, ce serait après imagecreatetruecolor.
RisingSun
Vous vous demandez pourquoi est alphablending = falseimportant ici? Le document l'indique ... "Vous devez désactiver alphablending ( imagealphablending($im, false)), pour l'utiliser."
quatre
2
Non seulement cette réponse est correcte et utile, mais elle est particulièrement utile car le premier commentaire (au moment de la rédaction) sur la documentation PHP imagecreatefrompng()suggère qu'il imagealphablendingfaut définir true, ce qui est clairement faux. Merci beaucoup.
spikyjt
3
Cette solution, dans mon cas GD fonctionne bien UNIQUEMENT si PNG a une zone de transparence "régulière", comme une zone transparente environnante, si elle a une zone complexe, comme avec les parties internes de l'image avec transparence, elle échoue toujours et met un fond noir , par exemple cette image échoue: seomofo.com/downloads/new-google-logo-knockoff.png . Quelqu'un peut-il essayer et confirmer?
aesede le
2
Ne semble pas fonctionner avec certains fichiers png transparents. J'ai essayé de créer une image à partir d'un jpg et de copier un png transparent à l'intérieur. Comme l'aesede le souligne, le résultat est un carré noir.
RubbelDeCatc
21

Pourquoi compliquez-vous les choses? ce qui suit est ce que j'utilise et jusqu'à présent, il a fait le travail pour moi.

$im = ImageCreateFromPNG($source);
$new_im = imagecreatetruecolor($new_size[0],$new_size[1]);
imagecolortransparent($new_im, imagecolorallocate($new_im, 0, 0, 0));
imagecopyresampled($new_im,$im,0,0,0,0,$new_size[0],$new_size[1],$size[0],$size[1]);

la source
Cela n'a pas fonctionné, je reçois toujours un fond noir avec cette image: seomofo.com/downloads/new-google-logo-knockoff.png
aesede
J'essayais les deux solutions: la vôtre et celle ci-dessus avec plus de 150 votes. Vos solutions fonctionnent très bien pour les GIF. Ce qui précède fonctionne mieux avec les fichiers PNG tandis que votre solution perd l'anti-alias ce que vous pouvez voir le mieux lors de la création de vignettes (semble en bloc, pixelisée).
Jonny
11

Je pense que cela devrait faire l'affaire:

$srcImage = imagecreatefrompng($uploadTempFile);
imagealphablending($srcImage, false);
imagesavealpha($srcImage, true);

edit: Quelqu'un dans les revendications de la documentation PHP imagealphablendingdevrait être vrai, pas faux. YMMV.

ceejayoz
la source
2
En utilisant imagealphablendingsoit vrai ou faux, j'obtiens toujours un fond noir.
aesede le
PHP7 - Travailler pour moi
Yehonatan
Joué avec (PHP 7.x): PNG: imagealphablending ($ targetImage, false); // si vrai sur les PNG: fond noir GIF: imagealphablending ($ targetImage, true); // si faux sur les GIF: fond noir
Jonny
9

Un ajout qui pourrait aider certaines personnes:

Il est possible de basculer imagealphablending lors de la création de l'image. Dans le cas précis où j'en avais besoin, je voulais combiner des PNG semi-transparents sur un fond transparent.

Commencez par définir imagealphablending sur false et remplissez la nouvelle image en couleur vraie avec une couleur transparente. Si imagealphablending était vrai, rien ne se passerait car le remplissage transparent fusionnerait avec l'arrière-plan noir par défaut et donnerait du noir.

Ensuite, vous basculez imagealphablending sur true et ajoutez des images PNG au canevas, laissant une partie de l'arrière-plan visible (c'est-à-dire ne remplissant pas l'image entière).

Le résultat est une image avec un fond transparent et plusieurs images PNG combinées.

Jorrit Schippers
la source
6

J'ai créé une fonction pour redimensionner l'image comme JPEG / GIF / PNG avec copyimageresampleet les images PNG y conservent toujours la transparence:

$myfile=$_FILES["youimage"];

function ismyimage($myfile) {
    if((($myfile["type"] == "image/gif") || ($myfile["type"] == "image/jpg") || ($myfile["type"] == "image/jpeg") || ($myfile["type"] == "image/png")) && ($myfile["size"] <= 2097152 /*2mb*/) ) return true; 
    else return false;
}

function upload_file($myfile) {         
    if(ismyimage($myfile)) {
        $information=getimagesize($myfile["tmp_name"]);
        $mywidth=$information[0];
        $myheight=$information[1];

        $newwidth=$mywidth;
        $newheight=$myheight;
        while(($newwidth > 600) || ($newheight > 400 )) {
            $newwidth = $newwidth-ceil($newwidth/100);
            $newheight = $newheight-ceil($newheight/100);
        } 

        $files=$myfile["name"];

        if($myfile["type"] == "image/gif") {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefromgif($myfile["tmp_name"]);
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagegif($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con){
                return true;
            } else {
                return false;
            }
        } else if(($myfile["type"] == "image/jpg") || ($myfile["type"] == "image/jpeg") ) {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefromjpeg($myfile["tmp_name"]); 
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagejpeg($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con) {  
                return true;
            } else {
                return false;
            }
        } else if($myfile["type"] == "image/png") {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefrompng($myfile["tmp_name"]);
            imagealphablending($tmp, false);
            imagesavealpha($tmp,true);
            $transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127);
            imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent); 
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagepng($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con) {
                return true;
            } else {
                return false;
            }
        }   
    } else
          return false;
}
pricopz
la source
3
Il est assez onéreux de lire tout le code pour comprendre pourquoi la transparence est préservée dans ce code par rapport au code de la question.
eh9
J'ai sauté ces deux lignes et cela fonctionnait toujours: $transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127); imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent);
santiago arizti
Cette réponse ressemble beaucoup à stackoverflow.com/a/279310/470749 .
Ryan le
5

Je suppose que cela pourrait faire l'affaire:

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile );

$targetImage = imagecreatetruecolor( 128, 128 );

$transparent = imagecolorallocate($targetImage,0,255,0);
imagecolortransparent($targetImage,$transparent);
imagefilledrectangle($targetImage,0,0,127,127,$transparent);

imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );

L'inconvénient est que l'image sera dépouillée de tous les 100% de pixels verts. Quoi qu'il en soit, j'espère que cela aide :)

Linus Unnebäck
la source
Si vous définissez une couleur extrêmement laide que presque aucune image n'utiliserait, cela peut être très utile.
Cory
1
La réponse acceptée n'a pas fonctionné pour moi. L'utilisation de cette réponse a imagecreate(...)fonctionné cependant. Vous créez une image qui est remplie avec la première couleur que vous attribuez. Ensuite, vous définissez cette couleur sur transparent. Si alphablending est défini sur true pour l'image cible, les deux images seront fusionnées et la transparence fonctionnera correctement.
Sumurai8
2

En reclassant la transparence de conservation, alors oui comme indiqué dans d'autres articles, imagesavealpha () doivent être définis sur true, pour utiliser l'indicateur alpha, imagealphablending () doit être défini sur false sinon cela ne fonctionne pas.

J'ai également repéré deux choses mineures dans votre code:

  1. Tu n'as pas besoin d'appeler getimagesize() pour obtenir la largeur / hauteur deimagecopyresmapled()
  2. Le $uploadWidthet $uploadHeightdevrait être -1la valeur, puisque les coordonnées commencent à 0et non 1, donc il les copierait dans un pixel vide. Le remplacer par: imagesx($targetImage) - 1et imagesy($targetImage) - 1, relativement devrait faire :)
Kalle
la source
0

Voici mon code de test total. Ça marche pour moi

$imageFileType = pathinfo($_FILES["image"]["name"], PATHINFO_EXTENSION);
$filename = 'test.' . $imageFileType;
move_uploaded_file($_FILES["image"]["tmp_name"], $filename);

$source_image = imagecreatefromjpeg($filename);

$source_imagex = imagesx($source_image);
$source_imagey = imagesy($source_image);

$dest_imagex = 400;
$dest_imagey = 600;
$dest_image = imagecreatetruecolor($dest_imagex, $dest_imagey);

imagecopyresampled($dest_image, $source_image, 0, 0, 0, 0, $dest_imagex, $dest_imagey, $source_imagex, $source_imagey);

imagesavealpha($dest_image, true);
$trans_colour = imagecolorallocatealpha($dest_image, 0, 0, 0, 127);
imagefill($dest_image, 0, 0, $trans_colour);

imagepng($dest_image,"test1.png",1);
Md. Imadul Islam
la source
0

Faites attention à l'image source widthet aux heightvaleurs transmises à la imagecopyresampledfonction. S'ils sont plus grands que la taille réelle de l'image source, le reste de la zone d'image sera rempli de couleur noire.

Stefan
la source
0

J'ai combiné les réponses de ceejayoz et Cheekysoft, ce qui a donné le meilleur résultat pour moi. Sans imagealphablending () et imagesavealpha (), l'image n'est pas claire:

$img3 = imagecreatetruecolor(128, 128);
imagecolortransparent($img3, imagecolorallocate($img3, 0, 0, 0));
imagealphablending( $img3, false );
imagesavealpha( $img3, true );
imagecopyresampled($img3, $srcImage, 0, 0, 0, 0, 128, 128, $uploadWidth, $uploadHeight);
imagepng($img3, 'filename.png', 9);
Marco
la source