Como crear vistas previas escaladas en PHP

martes 28 septiembre 2010 | In English

Siempre es una buena idea generar tantos tamaños de vistas previas como sean requeridas para un sistema basado en web, justo después de que la imagen es transferida al servidor. Algunas de las ventajas de generar y guardar las vistas previas son que se reduce el tiempo de procesamiento que necesita el servidor si se generan las vistas previas al ser requeridas, y por su puesto se puede reducir el consumo de ancho de banda al no enviar una imagen de tamaño completo cuando esto no es necesario.

Temas
Objetivo, requerimientos y uso
Creando la función
Notas finales
Descarga la función

Objetivo, requerimientos y uso

Nuestro objetivo aquí es que la imagen sea escalada de tal manera que quepa en un área rectangular, mientras mantiene la relación de aspecto. Vamos a definir un ancho y un alto máximos para la vista previa, y la imagen va a ajustarse a eso. Adicionalmente, podría dársele a la función solo el ancho o el alto máximos, para que de esta manera nuestra imagen sea escalada en base solo al valor provisto, ajustando el valor faltaste de tal manera que se mantenga la relación de aspecto de la imagen.

Usualmente, al subir una imagen al servidor de manera dinámica, guardamos el nombre de la misma en una base de datos, o le asignamos un nombre con el que podamos relacionarla a un registro; el siguiente ejemplo asume que obtenemos los datos del registro en una variable llamada $row["imagen"] y usamos esta para llamar la imagen:

<img src="/direccion/<?=$row["imagen"];?>" width="100" height="100" alt="" />

La idea es que en vez de llamar a la imágen de la manera anterior, podamos llamarla como a continuación, asumiendo que "mini" sea el prefijo que deseamos utilizar; también podemos utilizar algo como "mini100x100" si queremos, podría ser útil si generamos más de un tamaño, dándole a cada tamaño su propio prefijo:

<img src="/direccion/mini_<?=$row["imagen"];?>" alt="" />

Nuestra función pedirá los siguientes valores:

  • La dirección de la imagen original: Esta imagen ya debe estar guardada en una carpeta dentro de el servidor donde esté hospedado este script de PHP, por lo tanto, esta función deberá ser llamada una vez que la transferencia de una imagen al servidor sea procesada.
  • El ancho máximo deseado de la vista previa, este podría ser un carácter comodín (*), en cuyo caso determinaríamos el ancho basados en la altura provista.
  • La altura máxima deseada de la vista previa, este podría ser un carácter comodín (*) en cuyo caso determinaríamos la altura basados en el ancho provisto.
  • Un prefijo (o podría usarse un sufijo si se desea, con un simple cambio al código) a ser añadido al nombre de la imagen original, para usar este nombre al guardar la nueva imagen.

Y será llamada de la siguiente manera, una vez que la imagen ya se encuentra en el servidor, ajustando la dirección relativa de la imagen, el tamaño, y el prefijo de la manera que deseamos:

crear_previo_escalado("../imagenes/".$nombre_imagen, 100, 100, "mini");

Creando la función

Lo que hacemos aquí es crear un búfer con la imagen original, calcular el tamaño de la vista previa (ya que le damos a la función un ancho y/o un alto máximos y no el tamaño final, de otra manera terminaríamos con la relación de aspecto incorrecta(, creamos un búfer de imagen secundario con el tamaño que calculamos donde vamos a guardar la imagen escalada, realizamos la operación de escalado, guardamos esta imagen y finalmente destruimos los búfer de las imágenes.

function crear_previo_escalado($direccion_imagen, $previo_ancho, $previo_alto, $prefijo) {

Comenzamos revisando que los parámetros son números enteros positivos o el carácter comodín (*), y que ambos parámetros no son el carácter comodín.

if ($previo_ancho === "*" && $previo_alto === "*") {
    echo "Ambos valores no deben ser un carácter comodín";
    exit(1);
}

if (!(is_integer($previo_ancho) && $previo_ancho > 0) && !($previo_ancho === "*")) {
    echo "El ancho es inválido";
    exit(1);
}

if (!(is_integer($previo_alto) && $previo_alto > 0) && !($previo_alto === "*")) {
    echo "El alto es inválido";
    exit(1);
}

Después de esto averiguamos que tipo de imagen es usando pathinfo(), y creamos el primer búfer con la imagen.

$extension = pathinfo($direccion_imagen, PATHINFO_EXTENSION);
switch ($extension) {
    case "jpg":
    case "jpeg":
        $imagen_original = imagecreatefromjpeg($direccion_imagen);
        break;
    case "gif":
        $imagen_original = imagecreatefromgif($direccion_imagen);
        break;
    case "png":
        $imagen_original = imagecreatefrompng($direccion_imagen);
        break;
    default:
        echo "Tipo de archivo inválido";
        exit(1);
        break;
}

Obtenemos el ancho y el alto de la imagen original:

$original_ancho = imageSX($imagen_original);
$original_alto = imageSY($imagen_original);

Cuando uno de los parámetros es el carácter comodín, solo calculamos el parámetro que nos hace falta. Pero cuando ambos parámetros son valores numéricos, necesitamos comparar la relación de aspecto de la imagen original y el de la vista previa, en este caso podríamos enfrentarnos a tres situaciones:

La relación de aspecto de las imágenes es la misma:

Una representación gráfica de la operación de escalado cuando la imagen original y la vista previa tienen la misma relación de aspecto.

En este caso no necesitamos recalcular ninguno de los valores, creamos la vista previa con las mismas medidas que recibimos como parámetros.

La imagen original es más ancha que la vista previa.

Una representación gráfica de la operación de escalado cuando la imagen original es más ancha que la vista previa.

En este caso tomamos el ancho dado como parámetro, y calculamos la altura utilizando una regla de tres, de tal manera que la imagen original quepa dentro de los parámetros dados para la vista previa sin perder la relación de aspecto.

La imagen original es más angosta que la vista previa.

Una representación gráfica de la operación de escalado cuando la imagen original es más angosta que la vista previa.

En este caso tomamos el alto dado como parámetro, y calculamos el ancho utilizando una regla de tres, de tal manera que la imagen original quepa dentro de los parámetros dados para la vista previa sin perder la relación de aspecto.

if ($previo_ancho === "*") {
    $previo_ancho = ceil($previo_alto * $original_ancho / $original_alto);
} else {
    if ($previo_alto === "*") {
        $previo_alto = ceil($previo_ancho * $original_alto / $original_ancho);
   } else {
        if (($original_ancho / $original_alto) < ($previo_ancho / $previo_alto)) {
            $previo_ancho = ceil($previo_alto * $original_ancho / $original_alto);
        } else {
            if (($original_ancho / $original_alto) > ($previo_ancho / $previo_alto)) {
                $previo_alto = ceil($previo_ancho * $original_alto / $original_ancho);
            }
        }
    }
}

Después de esto creamos el segundo búfer de imagen, como un área vacía, con ImageCreateTrueColor():

$imagen_destino = ImageCreateTrueColor($previo_ancho, $previo_alto);

Y generamos la vista previa con imagecopyresampled():

imagecopyresampled($imagen_destino, $imagen_original, 0, 0, 0, 0, $previo_ancho, $previo_alto, $original_ancho, $original_alto);

Tras esto guardamos la vista previa, preservando el tipo original de la imagen.

$archivo_destino = pathinfo($direccion_imagen, PATHINFO_DIRNAME) . "/";
$archivo_destino .= $prefijo . "_" . pathinfo($direccion_imagen, PATHINFO_FILENAME);
switch ($extension) {
    case "jpg":
    case "jpeg":
        imagejpeg($imagen_destino, $archivo_destino);
        break;
    case "gif":
        imagegif($imagen_destino, $archivo_destino);
        break;
    case "png":
        imagepng($imagen_destino, $archivo_destino);
        break;
}

Y finalmente destruimos los búfer de las imágenes con imagedestroy() y cerramos la función.

    imagedestroy($imagen_destino);
    imagedestroy($imagen_original);
}

Notas finales

Si queremos usar un sufijo en lugar de un prefijo, solo necesitamos reemplazar $prefijo con $sufijo en la declaración de la función, y cambiar la linea:

$archivo_destino .= $prefijo . "_" . pathinfo($direccion_imagen, PATHINFO_FILENAME);

por las lineas:

$archivo_destino .= pathinfo($direccion_imagen, PATHINFO_BASENAME);
$archivo_destino .= "_" . $sufijo . "." . $extension;

Descarga la función

Solo recuerda llamarla una vez que la imagen fue subida al servidor, y agregarle el prefijo (o sufijo) al nombre de la imagen para mostrar la vista previa.

previo_escalado.tar.gz
md5sum: 9061db8ce5c58bc2f7f13ad19709a075

Categorías: PHP, Programación