PNG transparentes con PHP y GD

En ocasiones ni siquiera con la documentación delante podemos lograr hacer aquello que nos proponemos. Y tan sólo después de muchas horas probando combinaciones logramos hallar el procedimiento adecuado para conseguirlo. Un claro ejemplo se nos planteó hace pocos días con la librería GD2 de PHP. En esta ocasión queríamos

Artículos recientes

En ocasiones ni siquiera con la documentación delante podemos lograr hacer aquello que nos proponemos. Y tan sólo después de muchas horas probando combinaciones logramos hallar el procedimiento adecuado para conseguirlo.

Un claro ejemplo se nos planteó hace pocos días con la librería GD2 de PHP. En esta ocasión queríamos conseguir una imagen PNG transparente para superponerla sobre otra de fondo pero descubrimos que no era tan sencillo como parecía...

Hoy intentaremos explicar que problemas se nos plantearon y cómo los solucionamos:

Nuestro primer intento consistía en crear una imagen y rellenarla con un rectangulo de igual area con un color con canal alfa utilizando imagecolorallocatealpha:

//tamaño  
$ancho=100;  
$alto =100;  
//color  
$fondo = array();  
$fondo['red']    =  0;  
$fondo['green']=  0;  
$fondo['blue']    =  0;  
$fondo['alfa']    =  63;  
//nueva imagen  
$gd = imagecreatetruecolor($ancho, $ancho);  
//color con canal alfa  
$fondo = imagecolorallocatealpha($gd, $fondo['red'], $fondo['green'], $fondo['blue'], $fondo['alfa']);  
//rellenamos toda la imagen con este color  
imagefilledrectangle ( $gd , 0 , 0 , $ancho , $ancho , $fondo );  
//cabecera  
header("Content-type: image/png");  
//salida conservando el canal alfa  
imagesavealpha($gd,true);  
//devolvemos datos al navegador  
imagepng($gd);  
//destruimos imagen  
imagedestroy($gd);

Pero enseguida comprobamos que de hecho no se producía ninguna transparencia. ¡Aunque realmente sí que lo hacía pero sobre el fondo por defecto negro que tiene la imagen al crearse con imagecreatetruecolor!

Pregunta: ¿Se puede crear una imagen con un fondo por defecto con cierto canal alfa?
Respuesta: No.

Desgraciadamente no es posible. Siempre se crea la imagen con un fondo negro por defecto. Normalmente cuando no usamos transparencias éste fondo desaparece bajo los colores que pintamos encima, formas, lineas, caracteres, etc. Así que no nos importa.

Pregunta: ¿Existe alguna solución?
Respuesta: Sí.

El truco que nos llevó cierto tiempo es el siguiente.

Por defecto cuando pintamos sobre una imagen GD cualquier elemento un cierto pixel se coloca encima del anterior superponiendose. Si el nuevo pixel tiene transparencia dejará ver detrás el viejo color. Este hecho es el que hace que cualquier transparencia se haga sobre ese maldito fondo negro.

Pero este comportamiento puede ser alterado usando la función imagealphablending, esta función cambia el metodo de pintado de cada pixel. Una vez establecida nuestra imagen con este valor a false cuando pintemos un nuevo pixel, éste no se superpondrá al anterior sino que borrará el existente, lo "machacará" ocupando su lugar. Esto será así mientras este modo esté activo.

Sabiendo lo anterior es fácil conseguir nuestro propósito inicial, sustituyendo todos los píxeles negros del fondo con los nuevos creados con el color con transparencia y canal alfa.

//tamaño  
$ancho=100;  
$alto=100;  
//color  
$fondo = array();  
$fondo['red']    =  0;  
$fondo['green']=  0;  
$fondo['blue']    =  0;  
$fondo['alfa']    =  63;  
//nueva imagen  
$gd = imagecreatetruecolor($ancho, $ancho);  
//fondo  
//modo sobreescritura de pixeles anteriores activado  
imagealphablending($gd,false);  
//color con canal alfa  
$fondo = imagecolorallocatealpha($gd, $fondo['red'], $fondo['green'], $fondo['blue'], $fondo['alfa']);  
//rellenamos (sustituimos) toda la imagen con este color  
imagefilledrectangle ( $gd , 0 , 0 , $ancho , $ancho , $fondo );  
//modo sobreescritura de pixeles anteriores desactivado  
imagealphablending($gd,true);  
//cabecera  
header("Content-type: image/png");  
//salida conservando el canal alfa  
imagesavealpha($gd,true);  
//devolvemos datos al navegador  
imagepng($gd);  
//destruimos imagen  
imagedestroy($gd);

Ahora la imagen tendrá tan sólo un fondo de color negro con un canal alfa al 50% (64 sobre 128).