A transparência da imagem PNG pode ser preservada ao usar a amostra de imagem GDlib do PHP?


101

O seguinte snippet de código PHP usa GD para redimensionar um PNG carregado pelo navegador para 128x128. Funciona muito bem, exceto que as áreas transparentes na imagem original estão sendo substituídas por uma cor sólida - preto no meu caso.

Mesmo que imagesavealphaesteja definido, algo não está certo.

Qual é a melhor maneira de preservar a transparência na imagem reamostrada?

$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 );

Respostas:


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

fez isso por mim. Obrigado ceejayoz.

observe que a imagem de destino precisa das configurações alfa, não a imagem de origem.

Editar: código de substituição completo. Veja também as respostas abaixo e seus comentários. Não há garantia de que seja perfeito de forma alguma, mas atendeu às minhas necessidades na época.

$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 );

5
FIY, isso precisa ser depois que a imagem de destino foi criada. Nesse caso, seria após imagecreatetruecolor.
RisingSun

Quer saber por que é alphablending = falseimportante aqui? O doc afirma que ... "Você tem que desmarcar alphablending ( imagealphablending($im, false)), para usá-lo."
oitenta

2
Esta resposta não é apenas correta e útil, é particularmente útil porque o primeiro comentário (no momento da redação) na documentação do PHP para imagecreatefrompng()sugere que imagealphablendingdeve ser definido true, o que está claramente errado. Muito obrigado.
spikyjt

3
Esta solução, no meu caso GD funciona bem SOMENTE se o PNG tiver uma área de transparência "regular", como uma área transparente circundante, se tiver uma área complexa, como as partes internas da imagem com transparência, sempre falha e coloca fundo preto , por exemplo, esta imagem falha: seomofo.com/downloads/new-google-logo-knockoff.png . Alguém pode tentar isso e confirmar?
aesede

2
Não parece funcionar com alguns arquivos png transparentes. Tentei criar uma imagem de um jpg e copiar um png transparente para dentro. Como aesede aponta, o resultado é um quadrado preto.
RubbelDeCatc

21

Por que você torna as coisas tão complicadas? o seguinte é o que eu uso e até agora fez o trabalho para mim.

$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]);

Não funcionou, ainda obtenho um fundo preto com esta imagem: seomofo.com/downloads/new-google-logo-knockoff.png
aesede

Estava tentando as duas soluções: a sua e a acima com mais de 150 votos. Suas soluções funcionam muito bem para GIFs. O acima funciona melhor com arquivos PNG enquanto sua solução está perdendo o anti-aliasing o que você pode ver melhor na criação de miniaturas (parece em blocos, pixelizada).
Jonny de

11

Eu acredito que isso deve resolver o problema:

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

editar: Alguém nas afirmações de documentos PHP imagealphablendingdeve ser verdadeiro, não falso. YMMV.


2
Usando imagealphablendingverdadeiro ou falso, sempre obtenho um fundo preto.
aesede

PHP7 - Trabalhando para mim
Yehonatan

Brinquei com ele (PHP 7.x): PNG: imagealphablending ($ targetImage, false); // se verdadeiro em PNGs: fundo preto GIF: imagealphablending ($ targetImage, true); // se falso em GIFs: fundo preto
Jonny

9

Um acréscimo que pode ajudar algumas pessoas:

É possível alternar a combinação de imagem e combinação durante a construção da imagem. No caso específico de que precisava disso, queria combinar alguns PNGs semitransparentes em um fundo transparente.

Primeiro você define imagealphablending como false e preenche a imagem true color recém-criada com uma cor transparente. Se imagealphablending fosse true, nada aconteceria porque o preenchimento transparente se mesclaria com o fundo preto padrão e resultaria em preto.

Em seguida, você alterna imagealphablending para true e adiciona algumas imagens PNG à tela, deixando parte do fundo visível (ou seja, não preenchendo a imagem inteira).

O resultado é uma imagem com fundo transparente e várias imagens PNG combinadas.


6

Fiz uma função para redimensionar imagens como JPEG / GIF / PNG com copyimageresamplee as imagens PNG ainda mantêm a transparência:

$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;
}

3
É bastante oneroso ler todo o código para descobrir por que a transparência é preservada neste código em relação ao código em questão.
eh9

Eu pulei essas duas linhas e ainda funcionou: $transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127); imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent);
santiago arizti

Essa resposta se parece muito com stackoverflow.com/a/279310/470749 .
Ryan

5

Suponho que isso possa resolver o problema:

$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 );

A desvantagem é que a imagem será removida de todos os pixels verdes de 100%. De qualquer forma, espero que ajude :)


Se você definir uma cor extremamente feia que quase nenhuma imagem usaria, pode ser muito útil.
Cory

1
A resposta aceita não funcionou para mim. Usar esta resposta com imagecreate(...)funcionou. Você cria uma imagem, que é preenchida com a primeira cor que você alocar. Então você define essa cor como transparente. Se alphablending for definido como true para a imagem alvo, ambas as imagens serão mescladas e a transparência funcionará corretamente.
Sumurai8,

2

Retirando a transparência de preservação, então sim, como afirmado em outras postagens, imagesavealpha () deve ser definido como verdadeiro, para usar o sinalizador alfa imagealphablending () deve ser definido como falso, caso contrário não funciona.

Também descobri duas coisas menores em seu código:

  1. Você não precisa ligar getimagesize()para obter a largura / altura paraimagecopyresmapled()
  2. O $uploadWidthe $uploadHeightdeve ser -1o valor, já que o cordinates começa em 0e não 1, então ele iria copiá-los em um pixel vazio. Substituí-lo por: imagesx($targetImage) - 1e imagesy($targetImage) - 1, relativamente, deve servir :)

0

Aqui está o meu código de teste total. Funciona para mim

$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);

0

Preste atenção à imagem de origem widthe aos heightvalores que são passados ​​para a imagecopyresampledfunção. Se forem maiores do que o tamanho real da imagem de origem, o restante da área da imagem será preenchido com a cor preta.


0

Combinei as respostas de ceejayoz e Cheekysoft, que deu o melhor resultado para mim. Sem imagealphablending () e imagesavealpha () a imagem não é clara:

$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);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.