Imágenes dinámicas en PHP

Paleta de colores

PHP dispone de varias extensiones para el tratamiento y generación de imágenes.

En esta entrada usaremos la extensión GD que permite crear y manipular imágenes en varios formatos, como GIF, PNG, JPEG, SWF, TIFF y JPEG2000.

Las utilidades de crear imágenes usando PHP son muchas. Yo, por ejemplo, las he usado para crear contadores o imágenes de botones y menús, que se pueden personalizar en función de opciones del usuario.

Crear imágenes

Un fichero php de imagen se comporta exactamente igual que una imagen. Por ejemplo, podemos insertar una imagen dentro de una página usando la etiqueta img, de cualquiera de estos modos:

<img src="imagen.jpg" width="800" height="600" alt="imagen">
<img src=imagen.php width="800" height="600" alt="imagen">

En el primer caso se muestra la imagen almacenada el el fichero "imagen.jpg". En el segundo se muestra la imagen generada por el script "imagen.php".

Lo que sigue es un ejemplo de imagen generada en php:

<?php
    // Representación de funciones
    // Mayo de 2013, Con Clase
    // Salvador Pozo
    header('Content-type: image/png');

    $im = ImageCreateTrueColor(640, 480);
    $azul = ImageColorAllocate($im, 128, 128, 255);
    $azul2 = ImageColorAllocate($im, 64, 64, 230);
    $verde = ImageColorAllocate($im, 0, 64, 0);
    $verde2 = ImageColorAllocate($im, 0, 200, 0);

    // Ejes de coordenadas:
    ImageLine($im, 0, 240, 640, 240, $verde2);
    ImageLine($im, 320, 0, 320, 480, $verde2);

    // Lineas de escala:
    for($i = 50; $i < 320; $i+=50) {
        ImageLine($im, 320+$i, 0, 320+$i, 480, $verde);
        ImageLine($im, 320-$i, 0, 320-$i, 480, $verde);
    }

    for($i = 50; $i < 240; $i+=50) {
        ImageLine($im, 0, 240+$i, 640, 240+$i, $verde);
        ImageLine($im, 0, 240-$i, 640, 240-$i, $verde);
    }

    $y0 = $y1 = 240;
    $x0 = $x1 = 0;
    for($x1 = 0; $x1 < 640; $x1++) {
        $y1 = 240 + 200*sin($x1/25);
        ImageLine($im, $x0, $y0, $x1, $y1, $azul);
        $x0 = $x1;
        $y0 = $y1;
    }

    $y0 = $y1 = 240;
    $x0 = $x1 = 0;
    for($x1 = 0; $x1 < 640; $x1++) {
        $y1 = 240 + 150*cos($x1/35);
        ImageLine($im, $x0, $y0, $x1, $y1, $azul2);
        $x0 = $x1;
        $y0 = $y1;
    }

    ImagePng($im);
    ImageDestroy($im);
?>
Gráfica PHP

Lo primero que se debe hacer es enviar una cabecera con el tipo de contenido de la imagen. En este caso se trata de una imagen en formato png, pero se pueden generar otros formatos, como gif o jpg.

El segundo paso consiste en crear un recurso de imagen, usando la función ImageCreateTrueColor, especificando las dimensiones de la imagen en pixels en los dos argumentos, ancho y alto. También podemos crear recursos de imagen a partir de imágenes existentes, usando funciones como ImageCreateFromJpeg o ImageCreateFromGif, por ejemplo. En esos casos no es necesario especificar dimensiones, sólo la ruta de la imagen a usar como fuente.

En este ejemplo hemos creado cuatro plumas de cuatro colores diferentes, usando ImageColorAllocate. En esta función, el primer argumento es el recurso de imagen que hemos creado antes, y los tres restantes definen los componentes rojo, verde y azul, respectivamente. Se puede usar la función ImageColorAllocateAlpha, y añadir un quinto argumento, con la componente de transparencia, α.

A continuación trazamos el dibujo deseado, la extensión gd proporciona varias funciones para trazar líneas, arcos, elipses, rectángulos, etc. Para más detalles, consulta la documentación de php.

Parametrizar imágenes

Una ventaja de generar nuestras propias imágenes es que podemos parametrizarlas.

Veamos, como ejemplo, cómo crear una imagen para un contador. Pasaremos como parámetros al script la longitud, en número de dígitos, el valor del contador, el tamaño y el color.

<img src=contador.php?con=1234&lon=6&tam=20&col=114422 width="111" height="24" alt="1234">

El código para este script puede tener esta forma:

<?php
    // Genera imagen de contador
    // lon: longitud en dígitos
    // con: valor del contador
    // tam: tamaño de la fuente
    // col: color en hexadecimal: rrggbb
    // Mayo de 2013, Con Clase
    // Salvador Pozo

    Header("Content-type: image/png");

    if(isset($_GET["lon"])) {
      $lon = (int)$_GET["lon"];
    } else $lon = 4;
    if(isset($_GET["con"])) {
      $con = (int)$_GET["con"];
    } else $con = 0;
    if(isset($_GET["tam"])) {
      $tam = (int)$_GET["tam"];
    } else $tam = 15;
    if(isset($_GET["col"])) {
      $col = "0x".$_GET["col"];
    } else $col = "0x00c000";

    $b = (0+$col) % 256;
    $col = ($col-$b)/256;
    $g = $col % 256;
    $r = ($col-$b)/256;

    $contador = sprintf("%0".$lon."d", $con);
    $fuente = './fuentes/ariblk.ttf';
    $box = imageftbbox($tam , 0 , $fuente, $contador);
    $x = $box[2]-$box[0]+4;
    $y = $box[3]-$box[7]+4;
    $im = imagecreatetruecolor($x, $y);
    $azul = ImageColorAllocate($im, 128, 128, 255);
    $verde = ImageColorAllocate($im, 0, 64, 0);
    $verde2 = ImageColorAllocate($im, $r, $g, $b);

    imageline($im, 0,0,$x-1,0,$azul);
    imageline($im, 0,0,0,$y-1,$azul);
    imageline($im, 0,$y-1,$x-1,$y-1,$azul);
    imageline($im, $x-1,$y-1,$x-1,0,$azul);
    imageline($im, 1,1,$x-2,1,$verde);
    imageline($im, 1,1,1,$y-2,$verde);
    imageline($im, 1,$y-2,$x-2,$y-2,$verde);
    imageline($im, $x-2,$y-2,$x-2,1,$verde);

    $box = imageftbbox($tam , 0 , $fuente, "1");
    imagettftext($im, $tam, 0, -1, -$box[5]+1, $verde2, $fuente, $contador);

    ImagePng($im);
    ImageDestroy($im);
?>
Contador PNG

El resultado es mejorable, por supuesto. Puedes comprobar que para diferentes tamaños los dígitos no se ajustan bien al marco. Esto es debido a pequeñas diferencias en las anchuras de los caracteres, y al modo en que se trazan los textos, ya que las coordenadas se refieren a la esquina inferior izquierda del texto, en lugar de a la superior izquierda.

Para este ejemplo se ha usado una fuente truetype llamada 'ariblk.ttf', que se almacena en una carpeta local llamada fuentes. Puedes usar una fuente de sistema o usar la fuente que prefieras copiándola a la carpeta adecuada.

Modificar imágenes

Otro uso habitual consiste en modificar imágenes para añadir efecto o insertar marcas de agua. Una marca de agua es una segunda imagen que se superpone con la que queremos modificar, pero intentando que no dificulte demasiado su visión. Son difíciles de eliminar, y se suelen usar para evitar que las imágenes sean usadas por terceras personas. Generalmente se añade un logotipo o un texto que identifica al propietario de la imagen.

<img src="marcaagua.php?img=imagen.jpg" width="640" height="480" alt="imagen">

En este ejemplo, redimensionaremos la imagen a 640x480 pixels y añadiremos un texto superpuesto.

<?php
    // Marcas de agua
    // Mayo de 2013, Con Clase
    // Salvador Pozo
    header('Content-type: image/jpg');
    $nombre_archivo = $_GET["img"];
    $im = ImageCreateTruecolor(640, 480);
    $im1 = ImageCreateFromJpeg($nombre_archivo);
    list($ancho, $alto) = GetImageSize($nombre_archivo);
    ImageCopyResized  ( $im, $im1, 0, 0, 0, 0, 640, 480, $ancho, $alto );
    ImageDestroy($im1);

    $color = ImageColorAllocateAlpha($im, 255, 255, 255, 110);
    $fuente = './ariblk.ttf';
    ImageTtfText($im, 80, 30, 50, 400, $color, $fuente, "Con Clase");
    ImageJpeg($im);
    ImageDestroy($im);
?>
Marca de agua
Marca de agua + B/N

El resultado es un tamaño de imagen predecible, con una marca de agua. Sin embargo, debemos tener cuidado con las proporciones de la imagen original, ya que al redimensionarla puede quedar distorsionada.

Se pueden modificar imágenes usando la función ImageFilter. En este caso deberemos tener la precaución de verificar si la función existe en nuestra versión de PHP, ya que no siempre es así:

if(function_exists('ImageFilter'))
{
    ImageFilter($im, IMG_FILTER_GRAYSCALE);
}

Este código, por ejemplo, convierte la imagen $im a escala de grises. Puedes consultar la documentación para ver los filtros disponibles.

Imágenes en hojas de estilo

Es posible crear hojas de estilo en las que usaremos imágenes de fondo para algunos elementos, como títulos, tablas, etc.

Esas imágenes pueden ser generadas mediante código PHP, y la hoja de estilo completa, también.

Empecemos por una página en la que se cargue una hoja de estilo generada mediante PHP:

<!DOCTYPE HTML>
<html>
<head>
   <link rel="StyleSheet" href="ejemplo.css.php?c1=002080&amp;c2=001040&amp;c3=0040c0&amp;c4=00ff00"
       title="Estilo dinamico" media="Screen" type="text/css"/>
</head>
<body>
<h1>Titulo</h1>
<p>Texto...</p>
</body>
</html>

Sólo definiremos un estilo para la etiqueta H1, por eso el código de la página sólo muestra un título, y un pequeño texto.

Lo importante aquí es que la hoja de estilo se carga desde un script PHP llamado "ejemplo.css.php". Le pasamos cuatro parámetros, con los colores que deseamos.

El código de la hoja de estilos es:

<?php
    // Hoja de estilo dinámica
    // Mayo de 2013, Con Clase
    // Salvador Pozo

    header("Content-type: text/css");
    $Col1 = $_GET["c1"];
    $Col2 = $_GET["c2"];
    $Col3 = $_GET["c3"];
    $Col4 = $_GET["c4"];

    print("h1 { background-image: ");
    print("url(h1.php?c1=".$Col1."&c2=".$Col2."&c3=".$Col3."&c4=".$Col4."); ");
    print("background-repeat: repeat-x; background-position: 0 0; ");
    print("height:28px; font-size:22px; font-weight:normal; padding-left: 50px; overflow:hidden; color: #".$Col4."; }\n");

?>

Ahora personalizamos la etiqueta "h1", usando una imagen de fondo generada mediante un script PHP, tal como hicimos con otras imágenes anteriores.

Con los parámetros indicados, la hoja de estilos tiene el siguiente contenido:

h1 { background-image: url(h1.php?c1=002080&c2=001040&c3=0040c0&c4=00ff00);
background-repeat: repeat-x;
background-position: 0 0;
height:28px;
font-size:22px;
font-weight:normal;
padding-left: 50px;
overflow:hidden;
color: #00ff00;
}

Hay que prestar especial atención a la primera línea: header("Content-type: text/css"). Esta es la que define el tipo de salida que genera PHP, en este caso, una hoja de estilo.

Para finalizar, el código PHP para h1.php es el siguiente:

<?php
    // Imagen de fondo para H1 dinámica
    // Mayo de 2013, Con Clase
    // Salvador Pozo

    function CrearColor($im, $color) {
         $r = HexDec(substr($color, 0, 2));
         $g = HexDec(substr($color, 2, 2));
         $b = HexDec(substr($color, 4, 2));
       return ImageColorAllocate($im, $r, $g, $b);
    }

    Header("Content-type: image/gif");

    $im = imagecreatetruecolor(1, 25);

    $col = array();
    $col[0] = CrearColor($im, $_GET["c1"]);
    $col[1] = CrearColor($im, $_GET["c2"]);
    $col[2] = CrearColor($im, $_GET["c3"]);
    $col[3] = CrearColor($im, $_GET["c4"]);

    imageline($im, 0, 0, 9, 0, $col[3]);
    for($i = 0; $i < 6; $i++) {
         imageline($im, 0, 1+$i*4, 9, 1+$i*4, $col[1]);
         imageline($im, 0, 2+$i*4, 9, 2+$i*4, $col[0]);
         imageline($im, 0, 3+$i*4, 9, 3+$i*4, $col[0]);
         imageline($im, 0, 4+$i*4, 9, 4+$i*4, $col[2]);
    }
    imageline($im, 0, 24, 9, 24, $col[3]);

    ImageGif($im);
    ImageDestroy($im);
?>
Salida en navegador

La gran ventaja: que se pueden generar todos los gráficos asociados a una página con diferentes combinaciones de colores, sin tener que crear ficheros de imágenes para cada caso.

Los colores que se usan en el fichero HTLM puede ser extraídos de una base de datos, o de un fichero de configuración. Las imágenes se generan de forma dinámica a partir de los scripts PHP y usando esos colores.

Otros usos

La generación dinámica de imágenes tiene otras muchas aplicaciones, por ejemplo, crear captchas. Es relativamente sencillo crear imágenes a partir de un texto, usando diferentes fuentes y colores, y distorsionando la salida, añadir fondos, ruido, etc.

Avatar generado

Otra aplicación es la creación de imágenes para usar como avatares. Probablemente has visto este tipo de avatares en foros y blogs. Se generan a partir de un código HASH, creado a su vez a partir de una dirección de correo o una IP.

QR conclase.net

También se puede usar la generación dinámica para crear códigos QR, como el de la izquierda, aunque esto resulta algo más complicado, y no por el diseño del gráfico, sino por los algoritmos que se usan para generarlos.