gfx/image.h
The things the reference builds around an image -- its storage type, its scale, and a cache of decoded ones -- in this SDK's terms: around a Bitmap.
#include <sonore/gfx/image.h>- 5types
- Guides: The native editor
On this page
Design notes
ImageType: one backing, named the way the reference names it
The reference has an image type because an image may live on the GPU or in main memory and the code drawing it must not care. Here everything is rasterised in software into one premultiplied RGBA Bitmap, on purpose -- identical pixels on every machine, no context to lose -- and what a window peer blits IS that bitmap. So NativeImageType and SoftwareImageType make the same thing and say so: they exist so code written against the reference's Image(format, w, h, clear, type) has a place to land, and so a cache can tell by type ID which kind somebody asked for. A Bitmap is always cleared -- clearImage false is accepted and ignored, because handing out memory that held another plugin's pixels is not an optimisation worth having.
ScaledImage: art and the density it was drawn for
A 2x knob strip drawn at 1x is twice the size it was meant to be; drawn at 2x on a 1x screen it is half the size and soft. The image alone cannot say which it is. ScaledImage carries the scale beside the pixels, draws at the LOGICAL size the art means, and picks, from a set of renditions, the one a display of a given density wants -- Backing::scale is that density.
ImageCache: decoded once, shared, and let go
An editor is opened and closed many times in a session, and several instances of a plugin show the same artwork. Decoding the same file per editor per instance is wasted startup and wasted memory. The cache hands out shared pointers keyed by a path, a hash of the bytes, or an id; an image is let go once nothing but the cache has held it or asked for it for the length of the timeout -- the reference's rule, so an editor closed and reopened within a few seconds finds its art still decoded. A byte budget bounds what the cache alone keeps: past it, unused images go oldest first. Images somebody still holds are never "evicted" -- dropping the cache's pointer would free nothing and only lose the sharing.
There is no timer thread. Every call purges what has expired, and an editor's own timer may call purge() so a cache nobody asks still empties.
Types 5
Where an image's pixels live, as the reference names it: the base of NativeImageType and SoftwareImageType.
Here every type makes the same premultiplied RGBA Bitmap -- see the file comment for why the distinction is kept at all. A type is stateless and may be used from any thread; getTypeID is what tells two apart.
class NativeImageType : public ImageType
The backing a window peer blits from -- here the same premultiplied RGBA bitmap as everything else, because that is what the platforms are given.
class SoftwareImageType : public ImageType
An image in main memory, drawn by the software rasteriser.
An image and the display density it was drawn for: 1 for ordinary art, 2 for @2x art. Mirrors the reference's ScaledImage; the file comment says why the scale has to travel with the pixels.
Holds the bitmap by shared pointer to const, so copies are cheap and an image from ImageCache can be wrapped without a copy. A scale of 0 or less is taken as 1. Drawing goes through a Graphics, on whatever thread paints.
Decoded images, shared between every editor and instance that asks for the same file, bytes or id. Mirrors the reference's ImageCache; when an image is let go, and why the cache has no thread of its own, is in the file comment.
What it hands out is a shared pointer to a const Bitmap: hold it for as long as it is drawn, and the cache never frees it from under you. Every call takes the cache's lock, so any thread may use it -- but the getters read and decode files, so not the audio thread. shared() is the process-wide one; a separate instance is for a test.