Shotwell Architecture Overview: Pixbuf and Thumbnail Caching
In order to quickly display representations of the backing photos, Shotwell uses two primary caches: pixbuf (which are full-sized bitmaps of the photo) and thumbnail (which are scaled down to smaller sizes, so many can be shown at once on the screen). Both use background threads to load/decode/scale, both so the operations don't interrupt the GTK main thread and so many can be scheduled in parallel. More information on threading can be found here.
1. Pixbuf Caching
The PixbufCache class is to schedule loading several pixbufs at once. Once loaded, the pixbufs are held by PixbufCache until the cache overflows (which is limited by a pixbuf count) or the image is modified or removed, in which case the pixbuf is dropped and a new load is scheduled. If a pixbuf for a particular photo is held in memory by PixbufCache, it will not reload it unless the force parameter is set to true. The pixbufs are held in a FIFO.
An important concept of PixbufCache is that all pixbufs it holds are for a particular Scaling (which is specified when constructed). In general, loading unscaled (full-sized) images is discouraged, as is re-scaling an already scaled image (which degrades quality). Scaled loads are more efficient because libjpeg can optimize the decode if its sized appropriately. (GDK takes advantage of this. See Photo Pipeline for more information.) Thus, if the Scaling changes (the user changes the window size, for example), the PixbufCache needs to be thrown out as the scaled pixbufs are now the wrong size.
PixbufCache is capable of prefetching both the transformed version of a photo (if it has been transformed) and its original.
Currently, there is no global PixbufCache, as it is only used by EditingHostPage to improve display times. If more classes begin using PixbufCache (i.e. SlideshowPage), it may make sense to factor this into an application-wide cache.
2. Thumbnail Caches
ThumbnailCache manages the thumbnails of a particular size (or scale) that are stored persistently on disk. There are multiple ThumbnailCache instances, one for each scale. Currently there are only two: one for 360px thumbnails and one for 128px thumbnails.
Unlike PixbufCache, ThumbnailCache maintains its cache based on the total bytes of the pixbufs rather than count. Although thumbnails are roughly the same size in terms of bytes, its desirable to tune the caches to never exceed a certain number of bytes, to control the application's memory footprint.
Unlike the backing photos (which are never modified), the thumbnails always represent the final view of the photo, transformed or not. Thus, when a photo is edited its thumbnails must be regenerated and saved to disk. This currently occurs in the main thread, but will be moved to background threads in the future.
When a photo is first imported into the library, it is imported as well to all existing ThumbnailCaches. When it is needed (by Thumbnail, for example), the ThumbnailCache class determines which instance to fetch from and gets it from there. (If a single thumbnail size was maintained -- 360px -- then the scaling operations to reduce it to smaller sizes, such as 64px, would be immense, especially since the smaller the thumbnail the more can fit on screen. Reducing from 128px is far more efficient.) Once loaded and decoded, it is held in a FIFO.
Thumbnails can be fetched in blocking or non-blocking mode. Obviously non-blocking mode is more suitable in situations where many need to be loaded at once.
The thumbnail pixbufs are maintained in memory full-sized rather than scaled.
ThumbnailCache does not currently monitor the LibraryPhoto objects for state changes and reflect them in the cache. LibraryPhoto notifies ThumbnailCache manually. This will probably change in the future.