Image collections



Owen asked me to post some information regarding bug 65902 here, in
the hope to get some feedback on the proposed API.


That bug is concerned with image file formats which contain not just a
single image, but a collection of related images. The most prominent
example is the Windows .ICO format, which may be met on the web in the
form of favicon files. Web browsers need to select an appropriately
sized image from the favicon file. 

While gdk-pixbuf currently supports one specific kind of multi-image
files, namely animations, it doesn't currently offer adequate support
for the task mentioned above. Galeon loads favicons using the
gdk-pixbuf .ICO loader (which selects a single image according to some
fixed criteria) and scales the resulting pixbuf to the desired size. 
This sometimes leads to poor image quality, even when the source .ICO 
contains an image of the required size. 

As minimal solution for the galeon problem, we could extend the .ICO
loader to support the recently added load-at-size functionality. This 
would be suboptimal, since it doesn't lift the restriction that only a 
single image can be loaded (in contrast to animations, where we load
all frames in one go). One can easily imagine galeon needing two sizes
of a favicon, e.g. for a bookmark menuitem and a notebook tab. Using
load-at-size, this requires loading the icon multiple times.

Another use-case which requires access to all images in a multi-image
file is to make the huge set of existing Windows icons in .ICO format
available for gtk apps by creating GtkIconSets from them.

Anders Carlsson and I coded up a more complete solution which enables
loaders to load a complete collection in one pass, similar to the
current support for animations. 

Out patch adds a new abstract class, GdkPixbufCollection, which is
basically a glorified GdkPixbuf*[]. The glorification consists of
a) being able to query the depth of the image source (this is necessary,
since .ICO files may contain multiple images of the same size, but
different depth, and this difference would otherwise be lost, because
a pixbuf is always 24/32bpp) and b) a way to obtain a single
representative image from the collection (this was added mainly
preserve the information which image the current .ICO loader would
choose).

There are four collection implementations: a trivial one which
directly wraps a GdkPixbuf*[], and implementations for .ICO, .ANI and
.GIF files. The .ANI and .GIF implementations provide a collection
interface to the corresponding animations. I originally planned to
planned to make this directly available via 

GdkPixbufCollection *
gdk_pixbuf_animation_get_collection (GdkPixbufAnimation *animation);

but this would require the addition of a get_collection method to the
GdkPixbufAnimation vtable, and thus break the ABI, since the
gdk-pixbuf classes have no padding. Therefore, the patch currently
only allows the creation of collections at load-time.  

The (currently private) image loader SPI had to be changed a little
bit to accomodate collections. There is a new load_collection method,
and the ModulePerparedNotifyFunc as acquired a a GdkPixbufCollection*
parameter. A somewhat ugly aspect of the current image loader SPI is
that the presence or absence of the load_animation or load_collection
methods is used to decided whether a module supports animations or
collections. This forced me to implement a non-incremental
load_collection for the .ICO loader as a trivial wrapper around the 
incremental methods.


Thanks for listening, Matthias


PS: I've attached a screenshot of Anders nice testpixbuf-collection demo
showing a gif animation.


PPS: Here is a sketch of the new API added by the patch:

/**
 * gdk_pixbuf_loader_get_collection:
 * @loader: A pixbuf loader
 *
 * Queries the #GdkPixbufCollection that a pixbuf loader is currently creating.
 * In general it only makes sense to call this function after the "area_prepared"
 * signal has been emitted by the loader. If the loader doesn't have enough
 * bytes yet (hasn't emitted the "area_prepared" signal) this function will 
 * return %NULL.
 *
 * Return value: The #GdkPixbufCollection that the loader is loading, or %NULL if
 not enough data has been read to determine the information.
**/
GdkPixbufCollection* gdk_pixbuf_loader_get_collection (GdkPixbufLoader *loader);

/**
 * gdk_pixbuf_collection_new_from_file:
 * @filename: Name of file to load.
 * @error: return location for error
 *
 * Creates a new collection by loading it from a file.  The file format is
 * detected automatically.  If the file's format does not support image
 * collections, then a collection with a single image will be created. 
 * Possible errors are in the #GDK_PIXBUF_ERROR and #G_FILE_ERROR domains.
 *
 * Return value: A newly-created collection with a reference count of 1, or 
 * %NULL if any of several error conditions ocurred:  the file could not be 
 * opened, there was no loader for the file's format, there was not enough 
 * memory to allocate the image buffer, or the image file contained invalid 
 * data.
 **/
GdkPixbufCollection * gdk_pixbuf_collection_new_from_file (const char         *filename,
							   GError             **error);


/** 
 * gdk_pixbuf_collection_get_n_pixbufs:
 * @collection: a #GdkPixbufCollection
 * 
 * Returns the size of the collection.
 *
 * Return value: the number of images in the collection.
 */
int                   gdk_pixbuf_collection_get_n_pixbufs     (GdkPixbufCollection *collection);

/**
 * gdk_pixbuf_collection_load_pixbuf:
 * @collection: a #GdkPixbufCollection
 * @i: the position of the image to return, must be between 0 and the length
 *    of the collection
 *
 * Returns a new reference to the @i<!-- -->th image of the collection. 
 * The caller of this function is responsible for calling g_object_unref()
 * on the image when done with it.
 *
 * Return value: the @i<!-- -->th image 
 */
GdkPixbuf           * gdk_pixbuf_collection_load_pixbuf       (GdkPixbufCollection *collection,
							       int                  i);

/**
 * gdk_pixbuf_collection_get_pixbuf_width:
 * @collection: a #GdkPixbufCollection
 * @i: the position of the image, must be between 0 and the length
 *    of the collection
 *
 * Returns the width of the @i<!-- -->th image of the collection.
 *
 * Return value: the width of the @i<!-- -->th image 
 */
gint                  gdk_pixbuf_collection_get_pixbuf_width  (GdkPixbufCollection *collection,
							       int                  i);

/**
 * gdk_pixbuf_collection_get_pixbuf_height:
 * @collection: a #GdkPixbufCollection
 * @i: the position of the image, must be between 0 and the length
 *    of the collection
 *
 * Returns the height of the @i<!-- -->th image of the collection.
 *
 * Return value: the height of the @i<!-- -->th image 
 */
gint                  gdk_pixbuf_collection_get_pixbuf_height (GdkPixbufCollection *collection,
							       int                  i);

/**
 * gdk_pixbuf_collection_get_pixbuf_depth:
 * @collection: a #GdkPixbufCollection
 * @i: the position of the image, must be between 0 and the length
 *    of the collection
 *
 * Returns the number of bits per pixel which were used to store the
 * @i<!-- -->th image of the collection.
 *
 * Note that this is not the same as the actual bpp of the @i<!-- -->th 
 * pixbuf, which is always 24 or 32. 
 *
 * Return value: the depth of the @i<!-- -->th image 
 */
gint                  gdk_pixbuf_collection_get_pixbuf_depth  (GdkPixbufCollection *collection,
							       int                  i);

/**
 * gdk_pixbuf_collection_load_static_image:
 * @collection: a #GdkPixbufCollection
 *
 * If the collection is really just a plain image (has only one image),
 * this function returns that image. If the collection contains multiple
 * images, this function returns a reasonable thing to display as a static
 * representation of the collection, which might be the first image, or 
 * something more sophisticated. 
 *
 * Return value: an image representing the collection 
 */
GdkPixbuf            *gdk_pixbuf_collection_load_static_image (GdkPixbufCollection *collection);




Attachment: collection.png
Description: Binary data



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]