domingo, 3 de junio de 2012

Manejo de transparencias en imagenes PNG y BLOB Mysql

Hace un tiempo tuve un inconveniente con los tipos de datos blob para mostrar imagenes de tipo png con transparencias. Si bien muchas personas en su  momento me criticaron por querer guardar imagenes en una base de datos, yo seguí empeñado en mi idea, y gracias a DIOS que seguí firme en mi convicción.

Si bien aparentemente hay que hacer más procesos por el hecho subir la imagen al servidor y convertir la imagen a tipo binario para poder guardarla  en la base de datos, me he dado cuenta que el rendimiento de mis páginas ha sido mejor, pues al mostrarlas no tengo que reconvertirlas, pues las muestro tal cual son, un dato binario, que por otro lado pesan mucho menos que una imagen de archivo físico.

Voy a mostrar el proceso para archivos de imagen de tipo png pero el procedimiento es análogo para imagenes de tipo jpeg y gif. He de acotar que todo está desarrollado bajo POO y aconsejo tener un poco de conocimiento sobre el tema para una mejor comprensión del ejemplo, pero igual puede ser utilizado en forma de script.

Pues bien, sin más ni menos, aquí lo que se necesita para hacer la conversión para guardar la imagen en base de datos y luego mostrarla.

Para convertir la imagen a blob necesitaremos la siguiente función:



/**
     * Convierte un archivo png en formato binario.
     *
     * @param string $archivo Url de la imagen.
     * @return binary Objeto de tipo blob.
     */
    public static function pngABlob ( $archivo ) {

        $imagenCreada = imagecreatefrompng ( $archivo );

        if ( imagealphablending ( $imagenCreada, true ) ) {//SI SE ESTABLECE EL CANAL ALPHA

            imagesavealpha ( $imagenCreada, true );
        }
        ob_start ();
        imagepng ( $imagenCreada );
        $imagen = ob_get_contents ();
        ob_end_clean ();
        return base64_encode ( $imagen );
    }


Explicando un poco, la función imagecreatefrompng crea una nueva imagen a partir de un archivo o de una URL.

La función imagealphablending establece el modo de mezcla para la imagen, debe llevar el segundo parámetro en "TRUE" para establecer el canal alpha de la imagen obtenida de la función anterior.

La función imagesavealpha permite guardar la información completa del canal alpha que se há creado con la función anterior, también debe llevar el segundo parámetro en "TRUE".

La función imagepng procesa la imagen obtenida.

Con la función ob_get_contents capturamos el contenido del búfer de salida del procesamiento de la función anterior.

Con la función ob_end_clean limpiamos el búfer para evitar errores y liberar memoria ;).

Y por último, con la función base64_encode que nos permite convertir nuestra imagen en memoria a un formato que pueda aceptar el campo de tipo blob de Mysql.

En este punto, ya podemos guardar nuestra imagen en la base de datos en un campo de tipo blob. No voy a explicar como hacer esta parte, pues ya  sabemos como guardar otro tipo de datos; el procedimiento es el mismo.

Ahora bien, para mostrarla, hacemos nuestra consulta a base de datos como de costumbre, lo único que cambia es la línea de código que viene a  continuación:


echo '<_img src="' . Imagenes::mostrarBlob ( $miImagen->getImagen () ) . '"/>';


Donde Imagenes es una clase abstracta que contiene la función que expliqué anteriormente y esta nueva que es mostrarBlob.$miImagen, es un objeto que contiene la consulta realizada a la base de datos, y el método getImagen devuelve especificamente la imagen blob
de la consulta.

Pero quien es esa función mostrarBlob?, pues aquí la tenemos.


public static function mostrarBlob ( $blob ) {

 $mime = Archivos::obtenerMimeTypeBlob ( $blob );
 return "data:" . $mime . ";base64," . $blob;
}


La cosa se nos esta complicando un poco, pero no importa, que lo que interesa aquí es aprender.

Ahora bien, para mostrarla correctamente, necesitamos saber el tipo de imagen a mostrar, que si bien sabemos que es de tipo png, el sistema no lo sabe. Para eso hacemos uso de una función que nos va a permitir el cometido.

Pero antes, saber que Archivos, es otra clase que agrupa funciones que permiten procesar archivos de diferentes tipos y el método obtenerMimeTypeBlob permite obtener el MIMETYPE de archivos guardados en base de datos con formato blob.

Por último, la línea de código return "data:" . $mime . ";base64," . $blob;
donde "data:" permite identificar el tipo de imagen (realmente el tipo de archivo), ";base64," nos permite indicar la codificación de nuestro dato blob y $blob el dato de la consulta.

Pero quien es obtenerMimeTypeBlob?, aquí su código:


public static function obtenerMimeTypeBlob ( $blob ) {
 $listaFirmas = array (
 "474946383761"=>"image/gif",
 "474946383961"=>"image/gif",
 "89504E470D0A1A0A"=>"image/png",
 "FFD8FFE0"=>"image/jpeg",
 "FFD8FFE1"=>"image/jpeg",
 "FFD8FFE8"=>"image/jpeg",
 "25504446"=>"application/pdf",
 "377ABCAF271C"=>"application/zip",
 "504B0304"=>"application/zip",
 );

 $blob = base64_decode ( $blob );

 $firma = substr ( $blob, 0, 60 );
 $firma = array_shift ( unpack ( "H*", $firma ) );

 foreach ( $listaFirmas as $firmaParcial => $Mime ) {

 if ( stripos ( $firma, $firmaParcial ) === 0 ) {

 return $Mime;
 }
 }
 return "application/octet-stream";
 }


Lo que hace esta función, es leer nuestro blob y obtener las coincidencias para los tipos de archivos indicados en el arreglo $listaFirmas,  solo lo he hecho para estos pocos archivos, pero es un comienzo ;). Al encontrar el mime correspondiente lo devuelve, si no, devuelve un tipo de mime genérico.

Pues bien, si piensan que la cosa está muy complicada porque utilizo muchas clases y no las hé colocado todas o completas, no es problema porque  si las metemos todas en una sola clase, igual funcionará este ejemplo sin ningún problema, pero de todas formas aquí les dejo una clase completa con todas las funciones necesarias :).


abstract class ManejoBlob {

 /**
 * Convierte un archivo png en formato binario.
 *
 * @param string $archivo Url de la imagen.
 * @return binary Objeto de tipo blob.
 */
 public static function pngABlob ( $archivo ) {

 $imagenCreada = imagecreatefrompng ( $archivo );

 if ( imagealphablending ( $imagenCreada, true ) ) {//SI SE ESTABLECE EL CANAL ALPHA

 imagesavealpha ( $imagenCreada, true );
 }
 ob_start ();
 imagepng ( $imagenCreada );
 $imagen = ob_get_contents ();
 ob_end_clean ();
 return base64_encode ( $imagen );
 }

 /**
 * Muestra un dato de tipo blob.
 *
 * @param binary $blob Dato de tipo blob.
 */
 public static function mostrarBlob ( $blob ) {

 $mime = Archivos::obtenerMimeTypeBlob ( $blob );
 return "data:" . $mime . ";base64," . $blob;
 }

 /**
 * Obtiene el mime type de un tipo de dato blob.
 *
 * @param binary $blob Dato de tipo blob.
 */
 public static function obtenerMimeTypeBlob ( $blob ) {
 $listaFirmas = array (
 "474946383761"=>"image/gif",
 "474946383961"=>"image/gif",
 "89504E470D0A1A0A"=>"image/png",
 "FFD8FFE0"=>"image/jpeg",
 "FFD8FFE1"=>"image/jpeg",
 "FFD8FFE8"=>"image/jpeg",
 "25504446"=>"application/pdf",
 "377ABCAF271C"=>"application/zip",
 "504B0304"=>"application/zip",
 );

 $blob = base64_decode ( $blob );

 $firma = substr ( $blob, 0, 60 );
 $firma = array_shift ( unpack ( "H*", $firma ) );

 foreach ( $listaFirmas as $firmaParcial => $Mime ) {

 if ( stripos ( $firma, $firmaParcial ) === 0 ) {

 return $Mime;
 }
 }
 return "application/octet-stream";
 }
}


PD. ¿Por qué la clase es de tipo abstract?, porque no requiere ser instanciada y así ahorramos memoria ;P

Hasta una próxima oportunidad.

martes, 14 de febrero de 2012

Una solución a los div's contiguos

Hoy después de mucho tiempo he vuelto a retomar la senda de la enseñanza.

Algo que siempre nos ha dado muchos dolores de cabeza ( sobre todo a mí ) al momento de maquetar nuestras páginas web, es el colocar un conjunto de capas (div) de forma continua dentro de otra capa.

Si bien una solución es el de definir la propiedad float:left o float:right, pues bien hoy y después de investigar un buen rato, he conseguido una forma un tanto más elegante de realizar este efecto sin mayores complicaciones y de forma más eficiente y menos dolorosa.

 Se que hay muchas paginas  y foros que hablan sobre esto, pero en verdad no había visto que emplearan esta solución y gracias a DIOS que mi afán de buscar siempre una forma más óptima de hacer las cosas me llevó hasta esta página que si bien ya la conocía y la he estudiado algo, no había prestado atención a las bondades de la propiedad display.

Solo hay que hacer los siguientes ajustes en nuestra css que definirá nuestras capas, el ejemplo que coloco es para tres columnas. Solo habría que ajustar el ancho de las columnas para insertar o eliminar capas.

CSS:


.capa1 {
    ...
    width: 25%;
    display: table-cell;
}
.capa2 {

    ...
    width: 50%;
    display: table-cell;
}

.capa3 {

    ...
    width: 25%;
    display: table-cell;
}


.capaPrincipal {
    ...
     width: 100%; 
    display: table;
}


HTML:


capa class="capaPrincipal"
     capa class="capa1" /capa
     capa class="capa2" /capa 
     capa class="capa3" /capa
/capa


Por razones de código tuve que cambiar el div por capa y quitarle los < y >, pero la idea se entiende.

Hasta una próxima oportunidad.

PD: Estaré montando una serie de tutoriales de como programar en POO con php.