The Imlib Programmers Guide



What is Imlib?

Imlib is a general Image loading and rendering library designed to make the task of loading images, and obtaining X-Windows drawables a simple task, as well as a quick one. It also aims to provide simple manipulation routines that might be desired for common operations.



Why use Imlib?

Imlib is display depth independent. It will load images and work in 24 bit internally, dithering, re-mapping or whatever is needed to generate X drawables ready for use on the display by programs. It can also handle rescaling images to different sizes very quickly, is configurable to be "kind" to the colormap on 8-bit displays, generate transparencies, load more than one image format (in fact Imlib will load any image format), and handle intelligent caching for naive programs.

So what are its features?

If compiled fully, Imlib can, without the help of any other applications, load PPM, PGM, TIFF, PNG, XPM, JPEG and EIM format images and preserve their transparency characteristics. If Imlib does not natively (native and foreign formats will be discussed later), Imlib can call upon a set of tools on the system to convert the image into a format Imlib can understand - thus widening its range of formats to any format that another utility supports. Currently Imlib supports both Imagemagick and the PBMPLUS utilities to do this. This loading is slower, but it means that Imlib does not refuse to load an image just because it does not know its format.

Imlib has image caches that allow the programmer to write naive code. Images are loaded and cached. If an image is in the cache, it will not be loaded again, but merely referenced from the cache. This can make naive programs work surprisingly fast.Imlib can rescale an image to any size (limit 32767 x 32767 pixels). This means small icons can be expanded in size via Imlib, or large images reduced to small thumbnails with a single function call.

Imlib has the ability to do gamma, brightness, contrast or arbitrary curve mapping of images to improve their visibility, or to modify their appearance. This allows for gamma correction, white-point correction and other advanced features all in software, without any need for this in hardware. It also allows the use of greyscale image templates to have their R, G and B channels modified via mapping tables to be colorized. This can allow for interfaces to use greyscale templates and let the user modify the color mapping for a color scheme.

Imlib can do basic Image modifications (flip and rotate by 90 degrees), which may be desired.

Imlib is highly optimized to give best results wherever possible at high speeds. It has many options to hike up its rendering speed depending on what your X Server and hardware setup can handle.

Imlib handles many display depths. It will work on monochrome, 2, 3, 4 ,5 ,6, 7, and 8 bit pseudocolor and static color displays. It also works in 15 bit, 16 bit (555 and 565) displays, and 24 bit displays. If the display is pseudocolor, Imlib will remap the image to a palette. Imlib has a config file - a system and a user one the user one taking preference over the system config. In this The user, or system administrator defines a palette file for Imlib to use. This file is a list of red , green and blue values to be used as a color set for Imlib to map to in pseudocolor. This list can be as large, or as small as desired (from 2 to 256 entries). By default Imlib comes with a table of about 40 entries, specifically chosen to contain colors commonly used in images and displays, rather than the more simplistic colorcube approach. This file can be edited, with entries taken out or added as desired. Imlib by default will dither using this colorset, if in 8 bit or less. It does not allocate a private colormap, but merely takes what colors it can get and compensates for this. Dithering can be turned off if it is not desired, or if extra speed is needed. Imlib will also seamlessly handle 15, 16 and 24 bits (with an optional High Quality mode for 15 and 16 bpp where Imlib will even dither in these modes to make smooth gradients look perfectly smooth). The end result is that images look as best they can, and also preserve colormaps, in all bit depths.

When re-rendering an image at different sizes, Imlib has an advanced pixmap cache that will re-use cached pixmaps if already rendered at that size, speeding up rendering for native programs by saving the extra rendering into a pixmap. Imlib has support for the MIT-SHM extension via shared images to speed up rendering. It also has support for the MIT-SHM shared pixmap extension, but is unable to practically use this due to some major design flaws in the shared pixmap extension. The support is there waiting for the day when this is fixed.

Imlib has both Xlib and GDK versions that can be installed simultaneously, share the same config file, and so can be used in both the Xlib low level environment and the GDK level environment.



How do I start programming using Imlib?

I suppose the best way to help you start using Imlib, is a small sample program. Here are two one using GDK, another using Xlib. (I will point out that Xlib is more powerful than GDK unlike GDK, an Xlib program can run on multiple displays, and Imlib supports this - you just have to initialize Imlib per display you connect to).

The following program is a very simple image viewer. It accepts an image file as an option, then displays this image in a window. Control-C will quit the program. You can resize the window and the image will be resized with it. There are two versions - one for Xlib + Imlib, the other for GDK + GDK Imlib.

This program is written for Xlib.
To compile:
      cc test.c -o test -I/usr/X11R6/include -I/usr/local/include
      -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXext -ljpeg -lpng
      -ltiff
      -lz -lgif -lm -lImlib


test-xlib-1.c

This program is written for GDK.
To compile:
      cc test.c -o test -I/usr/X11R6/include -I/usr/local/include
      -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXext -ljpeg -lpng
      -ltiff
      -lz -lgif -lglib -lgdk -lm -lgdk_imlib


test-gdk-1.c

We could now optimize these programs (which are longer than they need to be just as an exercise) to use Imlib a bit more. Note the Differences.

This program is written for Xlib.
To compile:
      cc test.c -o test -I/usr/X11R6/include -I/usr/local/include
      -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXext -ljpeg -lpng
      -ltiff
      -lz -lgif -lm -lImlib


test-xlib-2.c

This program is written for GDK.
To compile:
      cc test.c -o test -I/usr/X11R6/include -I/usr/local/include
      -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXext -ljpeg -lpng
      -ltiff
      -lz -lgif -lglib -lgdk -lm -lgdk_imlib


test-gdk-2.c

As a note for future code snippets: All data types beginning with Imlib are Xlib Imlib data types, those beginning with Gdk are GDK Imlib data types. All functions beginning with Imlib_ are Xlib Imlib functions and all functions beginning with gdk_ are GDK Imlib function calls. All calls perform the same functions and accept similar parameters - the differences are GDK and Xlib primitives, and the Xlib Imlib has an extra ImlibData parameter at the start of each function call. Examples of sibling data types and functions calls will be given together. Chose the appropriate one for your situation. You will notice we saved a fair few commands - that I have found my code littered with all over the place, by using a function in Imlib that does this for you. It shows that you can have full control over your programs, if you desire, or let Imlib do it for you.



Imlib's concept of an image

Imlib has a certain concept of what an image is. It is designed to be fast, fairly general and powerful.

For Imlib an image is:

An image is a large chunk of 24-bit pixel data in RGBRGBRGB format.

There is a second chunk of alpha channel data that matches the RGB chunk pixel for pixel, but currently is unused, provided for future expansion.

An image contains an optional RGB value that tags all pixels with that value as being transparent. This is specifically designed around X's on or off transparency and clip mask operations.

Images have a set of border pixels - if not set, the borders are all set to 0 pixels. The borders are a special set of pixels that are scaled slightly differently from the rest of the image when rendered to a pixmap.

Images have attached map-tables that can either be explicitly set, or are generated from the image's Gamma, Brightness and Contrast settings (and the same settings per Red, Green and Blue channel). These settings and map-tables are inherited upon loading from the system settings (currently all normalized - will be added to the configuration file and init soon).

The data in the 24-bit representation of an image is a large block of unsigned char bytes. The image has a pointer to this data which is in the order from top-left pixel to bottom right, scanline by scanline. Each pixel is an RGB triplet, with the bytes in the order Red Green Blue, so the array looks something like RGBRGBRGBRGBRGBRGB...

If our image is:
ImlibImage *im;
GdkImlibImage *im;


The pointer to the RGB data and the alpha data would be:
im->rgb_data; /* pointer to unsigned char RGB data */
im->alpha_data; /* pointer to unsigned char Alpha data */


The alpha data is currently unused and should be ignored - the pointer to it should be kept NULL.

The shape color is an RGB value for the pixel color that is to be considered transparent. This defines how Imlib renders the image and if it generates a mask pixmap on rendering. Certain image formats (GIF, TIFF, PNG, EIM and XPM) contain transparency information. This is inherited by Imlib, so there is no need to set the shape color. If it is not set, a program can set a shape color, or if it is not desired, it can be removed by setting the RGB values for the Shape Color to R,G,B -1,-1,-1.

The way to retrieve the shape color, examine it and set it is as follows. It is assumes images have been loaded, Imlib initialised etc. already:

ImlibData *id;
ImlibImage *im;
ImlibColor color;
int r,g,b;
Imlib_get_image_shape(id,im,&color);
r=color.r;
g=color.g;
b=color.b;
color.r=100;
color.g=50;
color.b=255;
Imlib_set_image_shape(id,im,&color);


GdkImlibImage *im;
GdkImlibColor color;
int r,g,b;
gdk_imlib_get_image_shape(im,&color);
r=color.r;
g=color.g;
b=color.b;
color.r=100;
color.g=50;
color.b=255;
gdk_imlib_set_image_shape(im,&color);


The Border attribute is where the programmer or user gets to choose border scaling attributes. The border values define margins around the image that are not to be scaled in that direction (ie the top and bottom borders do not get scaled vertically but retain their pixel sizes). The best way to explain is with an example.

Notice how this is the original image, with a hilighted bevel of 3 pixels on each side. This is the original and so, we want these borders to to remain 3 pixels wide when the image is scaled to a larger size. The original is 32x32 pixels.

Here is an example of the image scaled to 80x40 pixels, when the borders are set to 0 pixels notice how the bevels get scaled too, giving quite an un-aesthetic look? This is how a vanilla scaling algorithm would work.

Now take a look at this third example scaled to the same size, but it has the border attributes set to 3 pixels for top, bottom, left and right borders. This time the image looks correct.

Note with this diagram that the borders map out an inner and outer boxes with corner pieces, effectively dividing the Image into 9 segments. The borders need not be of the same width - as shown here, they can each be of a different size, allowing for shadows, and other effects that may exist in the image.

The image map-tables are a set of 3, 256 value lookup tables of unsigned char values. These are calculated form the image's brightness, gamma, and contrast settings for the image as a whole and each red, green and blue channel. You can set and get these values as follows:

ImlibData *id;
ImlibImage *im;
ImlibColorModifier mod;
double gamma,brightness,contrast;
Imlib_get_image_modifier(id,im,&mod);
gamma=(double)mod.gamma/256;
brightness=(double)mod.brightness/256;
contrast=(dobule)mod.contrast/256;
Imlib_set_image_modifier(id,im,&mod);


GdkImlibImage *im;
GdkImlibColorModifier mod;
double gamma,brightness,contrast;
gdk_imlib_get_image_modifier(im,&mod);
gamma=(double)mod.gamma/256;
brightness=(double)mod.brightness/256;
contrast=(double)mod.contrast/256;
gdk_imlib_set_image_modifier(im,&mod);


Note that the contrast, brightness and gamma values are in fact int's. A value of 1.0 (unmodified verbatim data) is in fact 256, so to use a value of 0.5 for any of these, you would have to use a value of 128 etc.

When you set the modifiers, the map-tables (the curves) are re-calculated for that image. You have to re-render the image to gain a pixmap with these settings in effect.

There are also identical modifier settings for the red, green and blue channels, so these can be used for white point adjustment and other functions.

It is also possible to gain direct access to these map-table curves and set the values in them manually for finer control. There are 3 tables - one for red, one for green and one for blue. The functions

  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
  gdk_imlib_set_image_red_curve
  gdk_imlib_set_image_green_curve
  gdk_imlib_set_image_blue_curve
  gdk_imlib_get_image_red_curve
  gdk_imlib_get_image_green_curve
  gdk_imlib_get_image_blue_curve


will set and get these. You pass the pointer to an array of 256 unsigned char's as the last parameter in each of these functions to have Imlib fill that table's contents or use that table's contents.

By default the map-table is linear if the global gamma, brightness and contrast settings are all normal (ie at 256). This would produce a mapping table such as the one here on the left. There is a table per channel describing this mapping. If you want you could set the table per red, green and blue channel to perhaps something like the graph below, that would then give a more interesting mapping. The tables for each channel can be different, and so could be used for re-coloring images in interesting ways.

It is also possible to then use these mappings to in fact modify the original 24-bit data for the image - making the changes permanent, and thus not needing and of the modifiers. To do this you would call the Imlib_apply_modifiers_to_rgb or gdk_imlib_apply_modifiers_to_rgb functions.If you need advanced manipulation of the 24-bit data (eg blurring, other convolutions etc.) you may do this to your heart's content. Just remember - before you render any pixmaps to call Imlib_changed_image or gdk_imlib_changed_image which will prevent Imlib's caching form not updating the pixmaps. This marks all pixmaps rendered off that image as being "dirty" and so they will never be referenced again by Imlib's caching. Once they are all freed and their reference counts are zero, they will be freed from memory.
author: The Rasterman