17 años en Internet

20 agosto 2013

Mostrando un PNG con pnglite (Aplicación C)

Hace un par de semanas os enseñé cómo hacer una aplicación que os permita realizar una captura de pantalla y volver su contenido a un fichero PNG. Hoy os voy a enseñar la inversa, cómo crear una ventana X11 que nos permita visualizar un fichero PNG.



Para ello, he tomado prestado el código fuente del PngView de Vegard Edvardsen y lo he adaptado acorde a las necesidades que buscamos. En este caso para compilar nuestra aplicación, además del paquete build-essential, vamos a necesitar descargarnos los paquetes libpnglite-dev libx11-dev zlib1g-dev para poder compilar nuestra aplicación.

El primer paso es definir la función principal main, que será la encargada de iniciar las librerías de pnglite a través de png_init. Básicamente esta función hace que la librería reserve una serie de memoria dinámica para sus quehaceres, si no la invocáramos obtendríamos violaciones de segmento cuando tratáramos de acceder a los datos del PNG que fuéramos a cargar. De paso aprovechamos para recuperar por argumento de entrada el nombre del fichero a cargar, abrimos para lectura el PNG con png_open_file_read y lo pasamos por referencia a la función  png_display, la cual no pertenece a pnglite, si no que la vamos a crear nosotros.


Por su parte, empezamos la cocción de la función png_display, recibiendo por referencia el png deseado y el nombre del archivo (que emplearemos para poner un título a la ventana).

Entre las variables que emplearemos tenemos un string title donde montaremos el título de la ventana, un vector de chars sin signo donde guardar el contenido del PNG, un puntero a Display que contendrá la estructura de la pantalla por defecto, además de un puntero a Visual y un concentrador gráfico GC que nos permitirá dibujar el PNG dentro de una ventana Win. También tenemos un puntero a XImage, que nos permitirá crear un mapa de bits donde almacenar la imagen a insertar en el concentrador gráfico. Además contaremos con entero sin signo bpm que usaremos para calcular el bitmap pad de la imagen, un puntero a XSizeHints donde definiremos el tamaño fijo de la ventana, una estructura Atom para definir que la aplicación reciba notificación cuando cerremos la ventana y un XEvent que nos permita saber cuando debemos redibujar la ventana. Toma jeroma, espero que no os desmayarais leyendo todo esto.

Primero crearemos una ventana para nuestra pantalla por defecto, acto seguido le definiremos un título de forma dinámica y modificaremos el tamaño de la ventana para que sea fijo (no se pueda maximizar o restaurar) y que este tamaño contenga las dimensiones del PNG. Por último daremos orden de pintar la ventana en el servidor gráfico.


A continuación cargaremos el concentrador gráfico del gestor de ventanas y una estructura visual que contiene el tipo de visualización de la pantalla por defecto. Empleando nuestra estructura visual podremos crear una imagen de bits que posteriormente, en el bucle principal de más adelante, aplicaremos a nuestra ventana a través del concentrador gráfico. Profundizando más, el contenido del mapa de bits será el contenido del PNG devuelto en el formato que aplica la función png_get_data. Para tener menos problemas de visualización (puede dar problemas con imágenes que no sean true-color), calcularemos el valor del "bitmap pad" del mapa a partir de la profundidad del PNG. Estos problemas gráficos se debe a las limitaciones de la librería pnglite y no he encontrado forma rápida de solventarlos.


Hay que destacar que hemos creado un mapa de bits, pero aún no lo hemos pegado en la ventana. Antes de hacerlo tendremos que configurar la ventana creada para que atienda dos tipos de eventos: principalmente las órdenes de printado que nos pueda dar el entorno gráfico (servidor gráfico + gestor de ventanas) y el aviso de que se ha cerrado nuestra ventana. Una vez definamos que esperamos este tipo de eventos, crearemos un bucle infinito que atenderá uno a uno todos los eventos que recibamos al respecto (los obtenemos a través de XNextEvent y los tratamos en el switch).


Como hemos definido préviamente, vamos a gestionar dos tipos de eventos: órdenes de printado y notificaciones de cierre. La primera es básica, cuando recibamos un evento de tipo Expose dibujaremos nuestro mapa de bits en el concentrador gráfico de la ventana, haciendo uso de la función XPutImage. Por su parte, si el tipo de evento es ClientMessage habrá que liberar recursos y cerrar la aplicación:


Si toda esta explicación te ha resultado muy intragable, te adjunto aquí el código fuente para que puedas digerir más a gusto.

No hay comentarios:

Publicar un comentario

Si te ha gustado la entrada o consideras que algún dato es erróneo o símplemente deseas dar algún consejo, no dudes en dejar un comentario. Todo feedback es bienvenido siempre que sea respetuoso. También puedes contactarme vía Twitter @Hamster_ruso si lo consideras necesario.