Como crear vistas previas escaladas y recortadas en PHP
In English  

En la publicación anterior, creamos vistas previas escaladas de imágenes que mantenían la relación de aspecto. En esta publicación veremos como recortar y escalar la imagen para que el area de la vista previa quede llena.

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

Objetivo, requerimientos y uso

Nuestro objetivo aquí es que, dado el tamaño de la vista previa, escalemos la imagen original de tal manera que se ajuste al tamaño de la vista previa sin dejar espacio vacío, y todo lo que no entre en el área rectangular de la vista previa sea cortado.

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="" />

Requerimos cuatro valores para esto:

  • La dirección de la imagen: Esta imagen necesita estar ya guardada en una carpeta dentro de nuestro servidor, por lo tanto, esta función debe ser llama tras procesar la transferencia de la imagen al servidor.
  • El ancho deseado de la vista previa.
  • El alto deseado de la vista previa.
  • Un prefijo (o podría utilizarse un sufijo si se desea) que será añadido al nombre de la imagen original.

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_cortado("../imagenes/".$nombre_imagen, 100, 100, "mini");

Creando la función

Necesitamos crear un búfer de imagen con la imagen original, calcular si necesitamos cortar un área de la imagen y que tanto, crear un segundo búfer de imagen donde guardaremos la imagen escalada, realizar la operación de corte y escalado, guardar la nueva imagen y finalmente destruir los búfers de las imágenes.

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

Antes que nada revisamos que los parámetros son números enteros positivos.

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

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

A continuación averiguamos con que tipo de imagen estamos lidiando, una gif, una jpg o una png, usando pathinfo(), y creamos el primer búfer con la imagen original.

$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:
        exit(1);
        echo "Tipo de archivo invalido";
        break;
}

Tras cargar el búfer con la imagen original, obtenemos su ancho y su alto:

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

Ahora necesitamos calcular los parámetros faltantes para poder general la vista previa, reglas de tres son usadas aquí para calcular el ancho o el alto de el área que vamos a utilizar para general la vista previa, y entonces utilizamos una resta para encontrar las coordinadas desde donde vamos a cortar la imagen original para crear la vista previa. Podemos encontrarnos con tres diferentes situaciones:

La imagen original y la vista previa tienen la misma relación de aspecto.

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 simplemente asumimos que las coordenadas comienzan en 0, 0 y no realizamos ningún calculo, al escalar la imagen ajustará perfectamente en la vista previa.

if ((($original_ancho / $original_alto) - ($previo_ancho / $previo_alto)) == 0) {
    $original_x = 0;
    $original_y = 0;
}

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

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

En este caso nos quedamos con la altura de la imagen original, y recalculamos con una regla de tres el ancho con base en la relación de aspecto de la vista previa. Además, tomamos la coordenada y como 0, y calculamos la coordenada x. Si queremos que la vista previa comience desde la izquierda, podemos ajustar la coordenada x en 0, o si queremos que comience desde la derecha, podemos ajustar la formula como $original_x = $original_ancho - $temporal_ancho;

if (($original_ancho / $original_alto) > ($previo_ancho / $previo_alto)) {
    $original_y = 0;
    $temporal_ancho = ceil($original_alto * $previo_ancho / $previo_alto);
    $original_x = ceil(($original_ancho - $temporal_ancho) / 2);
    $original_ancho = $temporal_ancho;
}

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

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

En este caso mantenemos el ancho de la imagen original, y recalculamos la altura con una regla de tres, basados en la relación de aspecto de la vista previa. Tomamos la coordinada x como 0, y calculamos la coordinada y. Si queremos que la vista previa comience desde la parte de arriba, podemos poner la coordinada y como 0, o si queremos que comience desde abajo, podemos ajustar la formula a $original_y = $original_alto - $temporal_alto;

if (($original_ancho / $original_alto) < ($previo_ancho / $previo_alto)) {
    $original_x = 0;
    $temporal_alto = ceil($original_ancho * $previo_alto / $previo_ancho);
    $original_y = ceil(($original_alto - $temporal_alto) / 2);
    $original_alto = $temporal_alto;
}

Una vez que calculamos los valores necesarios, creamos el búfer para la vista previa con ImageCreateTrueColor():

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

Y creamos la vista previa con toda la información que calculamos, para esto utilizamos imagecopyresampled():

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

Una vez hecho esto, guardamos la imagen. Utilizamos la extensión que extrajimos previamente para generar la nueva 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_recortado.tar.gz
md5sum: a377dcb1dec64b77267ffe7109dc44de