Como desarrollador de tecnologías para móviles, uno de los servicios que hacemos habitualmente es la venta de imágenes, fondos, wallpapers o como queráis llamarlo. Este servicio requiere el escalado de los archivos para adaptarlo al tamaño de pantalla del teléfono del cliente. Además de para la descarga de contenidos, el redimensionamiento de imágenes es necesario también para adaptar el look&feel de los sites al dispositivo del cliente. En un primer momento lo hacíamos con Imagemagick, pero, por alguna extaña razón, a medida que aumentaban las visitas se saturaba el Apache de las máquinas (Windows) puesto que, parece ser, el convert no finalizaba los procesos, y había que reiniciar los servidores web. La solución fue reemplazar el sistema basado en Imagemagick por otro basado en GD, utilizando las funciones nativas de PHP, de manera que todo quedaba integrado en la propia aplicación.
Os estaréis preguntando cual es el problema en el escalado de imágenes con GD. Ninguno, el resultado era perfecto hasta que nos encontramos con gif’s transparentes. Al hacer el escalado se perdía la transparencia sin remedio. Dimos muchas vueltas y probamos distintos métodos que fuimos leyendo por cientos y cientos de foros y blogs, puesto que en la documentación de PHP no quedaba nada claro cómo hacerlo hasta que dimos con la solución.
He decidido separar este artículo en dos partes. En el segundo trataremos el escalado de gif’s animados, un proceso bastante más complejo que los estáticos.
En un GIF, la transparencia viene definida por un color, un solo color, que se define como transparente, de manera que cada pixel que tenga exactamente ese color será transparente, de ahí que, tradicionalmente, se comience por una imagen de fondo blanco o negro y sobre ella se dibuje, guardando ese color de fondo inicial como el de transparencia. La transparencia es una característica de la imagen, no del color en sí. No tiene por qué ser blanco o negro, cualquier color puede ser el transparente, pero una vez definido, cualquier pixel que tenga ese color será transparente. Por otro lado, en un GIF se guarda la paleta de colores que utiliza, de manera que se optimiza el tamaño de la imagen manteniendo la información únicamente de los colores disponibles y referenciándolos por un índice dentro de esa paleta. Esto que puede parecer un dato sin importancia es fundamental a la hora de tener claro el proceso a seguir desde PHP.
Hay varias funciones de PHP que hacen referencia a la transparencia y a los colores. En unas se utilizan los colores como tal en formato RGB, en otras se utiliza el índice de color en la paleta y en otras se necesita la representación del color. Aquí está uno de los problemas, ¿cómo se debe utilizar el resultado de cada una de las funciones disponibles?.
Lo primero que debemos calcular es el porcentaje de escalado que vamos a aplicar. Esto dependerá de nuestra aplicación y del destino de la misma. Si queremos fijar un determinado ancho habrá que calcular el porcentaje sobre el que debemos escalar el alto y viceversa. No entraremos en detalles sobre este aspecto puesto que se sale fuera del objeto de este artículo.
Una vez tenemos claro el nuevo tamaño, debemos, antes de nada, saber cual es el color transparente de la imagen original. Para ello usamos la función imagecolortransparent, si hay transparencia nos devolverá el identificador de ese color. A partir del índice del color transparente, obtenemos la representación RGB en la imagen original con imagecolorsforindex y ya podemos localizar ese color en la nueva imagen con imagecolorallocatealpha. Ya tenemos todo lo necesario. Ahora simplemente rellenamos la nueva imagen con ese color, le decimos que ese color será el transparente y copiamos la imagen original en la nueva escalando según necesitamos.
A la hora de copiar la imagen y escalarla no utilizamos imagecopyresampled ya que, a diferencia de imagecopyresized, hace una interpolazión para suavizar el resultado final. Esto, que en general es correcto, en el gif transparente provoca la deformación de pixeles propios de la interpolación, haciendo que se pierda parte de la trasparencia ya que estos pixeles no son del color exacto.
Este sería el código final.
$srcImage=imagecreatefromgif("demo.gif");
$new_w=160;
$new_h=114;
$dst_img=imagecreatetruecolor($new_w,$new_h);
$transcolor=imagecolortransparent($srcImage);
if($transcolor!=-1){
$trnprt_color = imagecolorsforindex($srcImage, $transcolor);
$trnprt_indx = imagecolorallocatealpha($dst_img, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue'], $trnprt_color['alpha']);
imagefill($dst_img, 0, 0, $trnprt_indx);
imagecolortransparent($dst_img, $trnprt_indx);
}
imagecopyresized($dst_img,$srcImage,0,0,0,0,$new_w,$new_h,ImageSX($srcImage),ImageSY($srcImage));
Para muestra, un color. Con esta imagen de fondo transparente (sí, vale que no es un Picasso, pero para el ejemplo sirve :P):
Conseguimos esto: