¿Se puede preservar la transparencia de la imagen PNG cuando se utiliza la imagen copyresampled GDlib de PHP?

101

El siguiente fragmento de código PHP usa GD para cambiar el tamaño de un PNG cargado en el navegador a 128x128. Funciona muy bien, excepto que las áreas transparentes en la imagen original están siendo reemplazadas por un color sólido, negro en mi caso.

Aunque imagesavealphaestá configurado, algo no está del todo bien.

¿Cuál es la mejor forma de preservar la transparencia en la imagen remuestreada?

$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
fuente

Respuestas:

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

lo hizo por mí. Gracias ceejayoz.

tenga en cuenta que la imagen de destino necesita la configuración alfa, no la imagen de origen.

Editar: código de reemplazo completo. Vea también las respuestas a continuación y sus comentarios. No se garantiza que esto sea perfecto de ninguna manera, pero logró mis necesidades en ese momento.

$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
fuente
5
FIY, esto debe ser después de que se haya creado la imagen de destino. En este caso, sería después de imagecreatetruecolor.
RisingSun
¿Se pregunta por qué es alphablending = falseimportante aquí? El documento lo dice ... "Tienes que desarmar alphablending ( imagealphablending($im, false)) para usarlo".
ochenta
2
Esta respuesta no solo es correcta y útil, sino que es particularmente útil porque el primer comentario (en el momento de escribir este artículo) sobre los documentos de PHP imagecreatefrompng()sugiere que imagealphablendingdebería establecerse true, lo cual es claramente incorrecto. Muchas gracias.
spikyjt
3
Esta solución, en mi caso GD funciona bien SOLO si PNG tiene un área de transparencia "regular", como un área transparente circundante, si tiene un área compleja, como con las partes internas de la imagen con transparencia, siempre falla y pone fondo negro , por ejemplo, esta imagen falla: seomofo.com/downloads/new-google-logo-knockoff.png . ¿Alguien puede probar esto y confirmar?
aesede
2
No parece funcionar con algunos archivos png transparentes. Intenté crear una imagen a partir de un jpg y copiar un png transparente dentro. Como señala Aesede, el resultado es un cuadrado negro.
RubbelDeCatc
21

¿Por qué haces las cosas tan complicadas? lo siguiente es lo que uso y hasta ahora me ha funcionado.

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

fuente
No funcionó, todavía obtengo un fondo negro con esta imagen: seomofo.com/downloads/new-google-logo-knockoff.png
aesede
Estaba probando ambas soluciones: la suya y la anterior con más de 150 votos. Tus soluciones funcionan muy bien para GIF. El anterior funciona mejor con archivos PNG, mientras que su solución es perder el suavizado de lo que puede ver mejor al crear miniaturas (parece bloqueado, pixelado).
Jonny
11

Creo que esto debería funcionar:

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

editar: Alguien en las afirmaciones de los documentos de PHP imagealphablendingdebe ser verdadero, no falso. YMMV.

ceejayoz
fuente
2
Al usar imagealphablendingverdadero o falso, siempre obtengo un fondo negro.
aesede
PHP7 - Trabajando para mí
Yehonatan
Jugué con él (PHP 7.x): PNG: imagealphablending ($ targetImage, false); // si es verdadero en PNG: fondo negro GIF: imagealphablending ($ targetImage, true); // si es falso en los GIF: fondo negro
Jonny
9

Una adición que podría ayudar a algunas personas:

Es posible alternar la combinación de alfabetos de imagen mientras se crea la imagen. En el caso específico de que necesitaba esto, quería combinar algunos PNG semitransparentes sobre un fondo transparente.

Primero, establezca imagealphablending en false y rellene la imagen de color verdadero recién creada con un color transparente. Si imagealphablending fuera verdadero, no pasaría nada porque el relleno transparente se fusionaría con el fondo negro predeterminado y resultaría en negro.

Luego, cambia imagealphablending a true y agrega algunas imágenes PNG al lienzo, dejando parte del fondo visible (es decir, sin llenar toda la imagen).

El resultado es una imagen con fondo transparente y varias imágenes PNG combinadas.

Jorrit Schippers
fuente
6

He creado una función para cambiar el tamaño de la imagen como JPEG / GIF / PNG copyimageresampley las imágenes PNG aún mantienen su transparencia:

$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
fuente
3
Es bastante oneroso leer todo el código para averiguar por qué se conserva la transparencia en este código sobre el código de la pregunta.
eh9
Me salté estas dos líneas y todavía funcionó: $transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127); imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent);
santiago arizti
Esta respuesta se parece mucho a stackoverflow.com/a/279310/470749 .
Ryan
5

Supongo que esto podría hacer el truco:

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

La desventaja es que la imagen se eliminará de cada 100% de píxeles verdes. De todos modos, espero que ayude :)

Linus Unnebäck
fuente
Si establece un color extremadamente feo que casi ninguna imagen usaría, puede ser muy útil.
Cory
1
La respuesta aceptada no funcionó para mí. Sin imagecreate(...)embargo, usar esta respuesta con funcionó. Creas una imagen, que se llena con el primer color que asignas. Luego, estableces ese color en transparente. Si alphablending se establece en true para la imagen de destino, ambas imágenes se fusionarán y la transparencia funcionará correctamente.
Sumurai8
2

Al volver a calificar la transparencia de conservación, entonces sí, como se indica en otras publicaciones, imagesavealpha () debe establecerse en verdadero, para usar la bandera alfa, imagealphablending () debe establecerse en falso, de lo contrario no funciona.

También vi dos cosas menores en su código:

  1. No necesita llamar getimagesize()para obtener el ancho / alto paraimagecopyresmapled()
  2. El $uploadWidthy $uploadHeightdebe ser -1el valor, ya que las coordenadas comienzan en 0y no 1, por lo que las copiaría en un píxel vacío. Reemplazarlo con: imagesx($targetImage) - 1y imagesy($targetImage) - 1, relativamente debería hacer :)
Kalle
fuente
0

Aquí está mi código de prueba total. Esto funciona para mi

$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
fuente
0

Preste atención a la imagen de origen widthy los heightvalores que se pasan a la imagecopyresampledfunción. Si son más grandes que el tamaño real de la imagen de origen, el resto del área de la imagen se rellenará con color negro.

Stefan
fuente
0

Combiné las respuestas de ceejayoz y Cheekysoft, que me dieron el mejor resultado. Sin imagealphablending () y imagesavealpha () la imagen no es 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);
Marco
fuente