Ese problema no es el único que se nos presentaba, ya que tampoco es fácil realizar la carga y descompresión de ficheros de tipo JPEG, GIF o PNG, ya que implica conocer su formato interno, realizar librerías de carga y descompresión LZW, fractal, inflate, etc. Todo esto es solucionable utilizando las librerías libjpeg, libgif, libungif, libpng o libtiff de Linux (así como otras disponibles), pero con la particularidad de disponer de un juego de funciones por cada una de ellas, y de que debemos distinguir en nuestro código el tipo de imagen a cargar para llamar a la función de carga y descompresión de la librería adecuada.
Para solucionar esto disponemos de la librería Imlib, la cual da acceso estas librerías a partir de una sola función de carga y que además, como veremos, dispone de funciones de conversión automática a la profundidad de color del display actual e incluso funciones de ampliación y reducción de imágenes, con lo que es posible utilizar iconos de cualquier tamaño, hacer thumbnails de imágenes, aumentar o reducir los pixmaps al tamaño deseado, etc. La versión de Imlib de la que dispone el autor es la 1.9.2, pero todo lo que expliquemos hoy será seguramente aplicable a otras versiones.
ftp://ftp.gnome.org/pub/imlib/TAR/imlib-1.9.2.tar.gzLos paquetes RPMs ya compilados (así como el .src.rpm de los fuentes) para sistemas Redhat, Suse o similares están disponibles en el mismo ftp site:
ftp://ftp.gnome.org/pub/imlib/RPM/imlib-1.9.2-1.i386.rpm ftp://ftp.gnome.org/pub/imlib/RPM/imlib-devel-1.9.2-1.i386.rpm ftp://ftp.gnome.org/pub/imlib/RPM/imlib-cfgeditor-1.9.2-1.i386.rpm ftp://ftp.gnome.org/pub/imlib/SRPM/imlib-1.9.2-1.src.rpmDado que Imlib permite acceso a los diferentes formatos gráficos más habituales para usarlos en nuestros programas, necesitaremos descargar e instalar (si no las tenemos ya) todas las librerías gráficas que dan control sobre los diferentes formatos para acceder nativamente a ellos (JPEG, GIF, PNG, TIFF, etc.). Las fuentes de las librerías (al menos en las versiones que tiene instalada el autor, y que puede cambiar con las diferentes actualizaciones que se van haciendo a las mismas) están disponibles en ftp.gnome.org bajo los siguientes nombres:
giflib-3.0-2.tar.gz libungif-3.0.tar.gz ibgr-2.0.13.tar.gz ibgr-scripts.tar.gz ibpng-1.0.1.tar.gz zlib-1.1.2.tar.gz tiff-v3.4-tar.gz jpegsrc.v6b.tar.gz gtk+-1.1.12.tar.gz glib-1.1.3.tar.gz ImageMagick-4.0.5.tar.gzSi en lugar de obtener los fuentes deseamos adquirir las librerías ya compiladas y listas para instalar, se deben descargar los siguientes paquetes:
giflib-3.0-2.i386.rpm libungif-3.0-3.i386.rpm libungif-devel-3.0-3.i386.rpm libungif-progs-3.0-3.i386.rpm ImageMagick-4.0.5-4.i386.rpm ImageMagick-devel-4.0.5-4.i386.rpm libgr-2.0.13-10.i386.rpm libgr-devel-2.0.13-10.i386.rpm libgr-progs-2.0.13-10.i386.rpm libjpeg-6b-5.i386.rpm libjpeg-devel-6b-5.i386.rpm libpng-1.0.1-1.i386.rpm libpng-devel-1.0.1-1.i386.rpm libtiff-3.4-4.i386.rpm libtiff-devel-3.4-4.i386.rpm zlib-1.1.2-1.i386.rpm zlib-devel-1.1.2-1.i386.rpm glib-1.1.3-1.i386.rpm glib-devel-1.1.3-1.i386.rpm gtk+-1.1.12-1.i386.rpm gtk+-devel-1.1.12-1.i386.rpmLas versiones de gtk+, gtk+-devel y glib pueden haber variado mucho dado su continuo desarrollo, al igual que con el resto de paquetes, por lo que se recomienda obtener siempre las ultimas versiones de los paquetes mencionados.
Los usuarios de Debian pueden perfectamente utilizar alien sobre los paquetes rpm y obtener así los .deb, compilar los ficheros .tar.gz, o simplemente ir a su ftp habitual de paquetes debian (por ejemplo, ftp.debian.org) desde donde obtener las librerías para su distribución Linux. Los que dispongan de rpm en su sistema simplemente deben ejecutar rpm -i *.rpm para realizar la instalación de todos estos paquetes. También es posible que ya dispongamos de todas estas librerías en nuestros sistemas (e incluso en el mismo CD de la distribución que usemos) de modo que podremos ponernos directamente a estudiar su uso práctico a nivel de programación.
ImlibImage *imagen; ImlibData *id; imagen=Imlib_load_image(id, "fichero.ext" );Imlib reconocerá el formato gráfico a carga por medio de la extensión atribuida al fichero gráfico, e introducirá los datos gráficos en las estructuras especificadas, cuyos campos podrán informarnos de las características de la misma:
anchura=imagen->rgb_width; altura=imagen->rgb_height; (etc...)Lo siguiente es, simplemente, renderizar o aplicar el bitmap (2 funciones según nuestras necesidades) sobre un Drawable dado (por ejemplo, una ventana o un elemento de tipo pixmap):
Imlib_apply_image(id, imagen, ventana);La función Imlib_render_image(id, imagen, anchura, altura) funciona mapeando el bitmap (internamente a 24bpp) sobre el pixmap especificado y con la anchura y altura deseada, mientras que Imlib_apply_image( id, imagen, ventana) realiza la misma función pero sobre una ventana y con la anchura y altura de la misma.
Las ventajas son muy claras. Gracias a Imlib podemos cargar todos nuestros gráficos en pixmaps durante la inicialización del programa para su posterior uso, y desde ficheros de formatos conocidos que podremos crear con cualquier herramienta de dibujo.
Veamos un ejemplo de uso de la librería Imlib basándonos en nuestro programa de ejemplo del mes (listado 1), poniendo en práctica todo lo que hemos visto en esta introducción a Imlib. Este programa carga el fichero gráfico que se le pase como parámetro (de cualquier formato) y lo visualiza en pantalla en la ventana que él mismo crea (como puede apreciarse en la figura 1, donde se visualizan varios ficheros GIF y JPG). Si mediante el uso del ratón modificamos el tamaño de la pantalla, la imagen se reducirá o ampliará automáticamente hasta ocupar la totalidad del área cliente de la misma. El programa finalizará al pulsar una tecla o mediante un ctrl+c en la xterm donde fue lanzado.
Los programas en Imlib se compilan como mínimo con las librerías libX11, libXext, libjpeg, libpng, libtiff, libz, libgif, libm e libImlib. Esto deja la orden de compilación de un programa cualquiera como:
gcc -o programa programa.c -I/usr/X11R6/include -I/usr/local/include -L/usr/X11/lib -L/usr/local/lib -lX11 -lXext -ljpeg -lpng -ltiff -lz -lgif -lm -lImlibListado 1:
/*-------------------------------------------------------------- Ejemplo.c Muestra como cargar, convertir y mapear imagenes mediante Imlib. Compilacion: gcc ejemplo.c -o ejemplo -I/usr/X11R6/include -I/usr/local/include -L/usr/X11/lib -L/usr/local/lib -lX11 -lXext -ljpeg -lpng -ltiff -lz -lgif -lm -lImlib (todo en una sola linea). ---------------------------------------------------------------*/ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/extensions/shape.h> #include <Imlib.h> int main(int argc, char **argv) { Display *display; ImlibData *id; XSetWindowAttributes attr; Window ventana; ImlibImage *imagen; int anchura,altura; XEvent evento; char done=0; /* Comprobamos el nº de parametros del programa: */ if (argc<=1) { printf("Visualizador de ficheros de imagenes:\n"); printf("Uso:\n %s fichero_de_imagen\n",argv[0]); exit(1); } /* Connectar al servidor X (display por defecto) */ display=XOpenDisplay(NULL); /* Inicializar Imlib para su uso con dicho display */ id=Imlib_init(display); /* Cargar la imagen especificada por el argumento argv[1] del programa. No es necesario indicarle a Imlib_load_image() en tipo de imagen pues lo averigua automaticamente a partir de la extension del fichero, lo cual permite carga de ficheros gif, jpeg, tiff, etc... */ imagen=Imlib_load_image(id, argv[1]); /* Extraer los datos de anchura y altura de la imagen original */ anchura=imagen->rgb_width; altura=imagen->rgb_height; /* Crear la ventana principal de nuestro programa (como en las anteriores entregas del curso. Ademas pedimos que se nos notifiquen los eventos de modificacion de la ventana (StructureNotifyMask) */ ventana=XCreateWindow(display, DefaultRootWindow(display), 0, 0, anchura, altura, 0, id->x.depth, InputOutput, id->x.visual, 0, &attr); XSelectInput(display, ventana, StructureNotifyMask | KeyPressMask ); /* Aplicamos la imagen en la ventana principal usando la anchura y altura de la ventana creada, y tras ello la visualizamos (XMapWindow()) */ Imlib_apply_image(id, imagen, ventana); XMapWindow(display, ventana); XSync(display, False); /* bucle de eventos (para las modificaciones de tamaño de ventana) */ while( !done ) { /* recoger el siguiente evento */ XNextEvent(display, &evento); /* si el evento es de tipo ConfigureNotify (resize) -> */ if (evento.type == ConfigureNotify) { /* entonces dibujamos de nuevo la imagen con el nuevo tamaño: */ Imlib_apply_image(id,imagen,ventana); XSync(display,False); } /* si se ha pulsado una tecla, salir */ else if( evento.type == KeyPress ) done = 1; } }
Imagen -> [byte r, byte g, byte b], [byte r, byte g, byte b], ... pixel(0,0) , pixel(1,0) , ...Además de este bloque de datos existe otro paralelo y separado del mismo que coincide con el bloque anterior pixel a pixel, llamado Alpha Channel (para transparencias y futuras ampliaciones de la librería). Otro dato asociado a las imágenes internamente es un valor RGB que es considerado como transparente, es decir, que a la hora de dibujar el pixmap se considera como hueco de la imagen y no sobreescriría el fondo donde se renderiza la misma. Además la imagen posee una serie de pixels en los bordes que por defecto se ponen a 0. Los bordes son pixels que son tratados de forma diferente al escalar la imagen cuando se renderiza en un drawable.
Finalmente, las imagenes en Imlib incluyen también look-up-tables que se utilizarán para generar las características de contraste, brillo y Gamma de la imagen. La estructura de datos para las imágenes en Imlib es, como veremos en la próxima entrega, ImlibImage. El acceso a los datos gráficos RGB de la imagen está determinado pues por 2 punteros que son campos de dicha estructura:
ImlibImage *imagen; imagen->rgb_data; -> Puntero a la imagen gráfica. imagen->alpha_data; -> Puntero al canal Alpha (normalmente NULL).Esto quiere decir que es posible también por nuestra parte acceder a los contenidos gráficos de la imagen (ver código del PutPixel para 24bpp en anteriores capítulos, utilizándolo para acceder a Imagen->rgb_data[pixel];). Tras realizar cualquier cambio en la imagen se deberá llamar a Imlib_changed_image() para indicarle que la imagen ha sido modificada y que se debe realizar un cacheo de la nueva imagen.
Si no deseamos disponer de ningún color que represente transparencia, simplemente habremos de poner las 3 componentes de Shape Color a (-1,-1,-1) (no transparencia). Para ello utilizaremos las funciones de lectura y especificación del ShapeColor:
Para obtener las componentes RGB del color de transparencia se procedería como sigue:
ImlibData *id; ImlibImage *imagen; ImlibColor color; int r,g,b; Imlib_get_image_shape(id, imagen, &color); r=color.r; g=color.g; b=color.b;Si deseamos cambiar ese color a cualquier otro (o a no transparencia) puede hacerse mediante:
color.r=-1; color.g=-1; color.b=-1; Imlib_set_image_shape(id, imagen, &color);
ImlibData *id; ImlibImage *imagen; ImlibColorModifier mod; double gamma, brightness, contrast; Imlib_get_image_modifier(id, imagen ,&mod); /* dividimos por 256 para obtener valores entre 0.0 y 1.0 */ gamma = (double) mod.gamma / 256; brightness = (double) mod.brightness / 256; contrast = (double) mod.contrast / 256;Para modificar los valores de brillo, contraste y gamma se dispone de la función opuesta:
/* se escriben en mod los valores de 0 (0.0) a 256 (1.0) */ Imlib_set_image_modifier(id, imagen, &mod);Como puede verse, operaciones como incremento o disminución de brillo o cambio del gamma (para monitores oscuros o claros) son en Imlib operaciones básicas y sencillas accediendo a los 3 campos de la estructura ImlibColorModified, que van desde 0 (valor de 0) hasta 256 (valor de 1.0). Tras eso las tablas son recalculadas para permitir otro posterior reajuste de las características de la imagen, debiendo re-renderizar de nuevo el pixmap para que los cambios surtan efecto al usuario.
Si se desea trabajar única y directamente sobre uno de los canales R, G o B se dispone de las siguientes funciones (reciben como parámetro un array de 256 unsigned char donde Imlib colocará los valores para las tablas, o desde donde los tomará):
Imlib_set_image_red_curve(), Imlib_set_image_green_curve(), Imlib_set_image_blue_curve(), Imlib_get_image_red_curve(), Imlib_get_image_green_curve(), Imlib_get_image_blue_curve(),
Santiago Romero