Progressive Loading with the Gdk-pixbuf Library
Author John R. Sheets explains how to use the progressive image-loading features of the gdk-pixbuf library.
Gdk-pixbuf's image-loading facilities make it very easy for you to load a variety of graphics file formats using the same interface. All you have to do is supply the file name, and gdk-pixbuf will handle the rest. However, the file-loading system has one potentially limiting flaw: It blocks until it's finished. With icons and smaller images, this temporary stall is not noticeable enough to matter, but when the file is particularly large, or is loading from a remote site with a slow connection, your application may flounder in an input/output loop, unable to update itself. The resulting slowness gives your application a buggy, unresponsive appearance, even though it has a good reason for stalling.
To address this problem, the gdk-pixbuf library offers the GdkPixbufLoader API. GdkPixbufLoader is a derivative of GtkObject that allows the loading process to be controlled by your application rather than by gdk-pixbuf's image-loading code. Because it's derived from GtkObject, GdkPixbufLoader can notify you through GTK+ signals when something interesting happens.
The interface is simple and straightforward. You create a loader object, write chunks of raw image data to it, and then close it when you're done. As soon as the loader receives enough of the graphics file to determine what sort of image it's loading, it creates a GdkPixbuf structure in which to store the image data. When you close the loader, it unreferences that GdkPixbuf, so if you want the pixbuf still to exist after you're done with the loader, your application will need to explicitly call gdk_pixbuf_ref() on it (and, of course, release it when you're done). The best place to reference the pixbuf is inside an area_prepared signal callback, which we'll discuss in a moment.
The loader uses the same code to load images into GdkPixbufLoader that gdk-pixbuf uses inside gdk_pixbuf_new_from_file(). Here's the basic GdkPixbufLoader API:
GdkPixbufLoader* gdk_pixbuf_loader_new(); gboolean gdk_pixbuf_loader_write(GdkPixbufLoader *loader, const guchar *buf, size_t count); GdkPixbuf* gdk_pixbuf_loader_get_pixbuf(GdkPixbufLoader *loader); void gdk_pixbuf_loader_close(GdkPixbufLoader *loader);
The loader has three signals of interest to us. Here are the prototypes for these signals, from the GdkPixbufLoaderClass structure:
void (* area_prepared) (GdkPixbufLoader *loader); void (* area_updated) (GdkPixbufLoader *loader, guint x, guint y, guint width, guint height); void (* closed) (GdkPixbufLoader *loader);
The aforementioned area_prepared signal indicates that the loader has read enough of the file to calculate the size and color depth of the image. Until the loader has this information, it makes no sense to create the target GdkPixbuf. This first milestone is important enough to merit a special signal emission because it means that your application can allocate space in the display for the image. If you connect to the area_prepared signal, you should take the opportunity in your callback to call gdk_pixbuf_ref() on the supplied pixbuf. Also, you are guaranteed at this point that the loader has not started writing into the new pixel buffer, so you can fill it with whatever background you want, whether it's a solid color or a placeholder image. As image data continues to pour into the loader, your background image will slowly be written over, line by line, until the full image arrives.
You'll receive only one area_prepared signal per loader, but altogether you'll end up receiving several area_updated signals. In fact, each time you call gdk_pixbuf_loader_write(), the loader will fire off another area_updated signal. If you're displaying the progressive image to a drawablewhich, after all, is largely the point of using GdkPixbufLoaderyou should use this opportunity to update the display. You can do this by copying the currently loaded portion of the image to a GdkPixmap drawable with gdk_pixbuf_render_to_drawable_alpha() and then triggering a refresh with gtk_widget_draw().
The final signal, closed, is useful if you want to know when the image is completely loaded. If other parts of your application, such as a Web browser window, need to be notified when the image is complete, you can connect them to the loader's closed signal, rather than blocking until then.
John R. Sheets has been following the GNOME project on a day-to-day basis for more than two years, since GNOME was only six months old. He is a software developer for CodeWeavers, Inc., where he works on Wine and GNOME. In his free time, he is helping the WorldForge Project to create a free online multiplayer gaming environment. John is the author of Writing GNOME Applications (Addison-Wesley, 2001), and the maintainer of the OpenBooks Web site.