[cairo] [RFC] replacing quartz image surface with generic cached representation concept
Paolo Bonzini
bonzini at gnu.org
Tue Nov 25 03:22:38 PST 2008
The quartz image surface seems to me like a special case of a more
generic "cached representation" concept usable by other OS-specific
surface types (e.g. glitz).
I'll present the concept at the same time as the possible API.
cairo_bool_t cairo_image_surface_get_cacheable (cairo_surface_t surf);
void cairo_image_surface_set_cacheable (cairo_surface_t surf,
cairo_bool_t cache);
These two functions mark the image as cacheable. Image surfaces
whose data is only modified using cairo, or that are explicitly
flushed after they're modified externally, can be cached.
The cache is a set of (reconstruct-func, destroy-func, cached-value,
up-to-date) tuples indexed by an integer key (usually another surface
type).
In the future, the cairo_bool_t might become an enumerator specifying
different cache policies.
void *_cairo_image_surface_get_cache (cairo_surface_t surf,
int key,
cairo_cache_reconstruct_func r,
cairo_cache_destroy_func d);
This function returns the cached representation of SURF that is
indexed by KEY. If the image is not cacheable, NULL is returned.
If there is a cached representation for KEY in SURF (either
up-to-date or not), R and D are not used and instead the
reconstruct-func and destroy-func in the cache are used.
If there isn't an up-to-date cached representation corresponding to
SURF, the reconstruct function is called immediately. If the
reconstruct function returns NULL, NULL is returned.
Otherwise the tuple (reconstruct-func, destroy-func, RESULT, true) is
stored in the cache.
void _cairo_image_surface_flush_cache (cairo_surface_t surf, int key);
This function eliminates the cached representation of SURF that is
indexed by KEY.
typedef void *(*cairo_cache_reconstruct_func) (void *old,
cairo_bool_t needed,
cairo_image_surface_t src);
The reconstruct function takes an image surface and returns the
value that _cairo_image_surface_get_cache will return. In this
case, it is called with NEEDED = true. OLD is the previous cached
value if there was a non-up-to-date cached representation, or NULL
if there was none.
Besides when _cairo_image_surface_get_cache is called, the
reconstruct function is called also when the surface is flushed.
In this case the NEEDED parameter will be false. If NULL is
returned, the cached representation will be marked as not up-to-date,
but the result field will *not* be modified (i.e. OLD will be passed
unchanged on the next call to the reconstruct function). If a
non-NULL value is returned, it replaces OLD in the cache and the
representation is marked up-to-date.
In either case, if NULL is returned but a non-up-to-date cached
representation was there, it is not eliminated (this may change
if different caching policies are implemented). The reconstruct
function can call _cairo_image_surface_flush_cache and return NULL
if it wants to achieve that.
typedef void (*cairo_cache_destroy_func) (void *data);
The destroy function is called by _cairo_image_surface_flush_cache
and upon surface destruction to free resources occupied by the
cached representation DATA. It is never called with NULL data.
---
An example usecase is given as changes to the cairo-quartz-surface.c
file. In _cairo_surface_to_cgimage, instead of special casing
CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
cairo_quartz_image_surface_t *surface =
(cairo_quartz_image_surface_t *) source;
*image_out = CGImageRetain (surface->image);
return CAIRO_STATUS_SUCCESS;
}
you would special case CAIRO_SURFACE_TYPE_IMAGE:
if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
CGImageRef image =
_cairo_image_surface_get_cache (source,
CAIRO_SURFACE_TYPE_QUARTZ,
_cairo_cgimage_reconstruct,
(cairo_cache_destroy_func) CGImageRelease);
if (image) {
*image_out = CGImageRetain (image);
return CAIRO_STATUS_SUCCESS;
}
}
And here is the reconstruct image callback, based on
cairo_quartz_image_surface_create:
CGImageRef oldImage = old;
CGImageRef newImage = NULL;
if (!needed)
return NULL;
if (!_cairo_quartz_verify_surface_size(width, height))
return NULL;
if (width == 0 || height == 0)
return NULL;
if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24)
return NULL;
/* To be released by the ReleaseCallback */
cairo_surface_reference (src);
newImage = _cairo_quartz_create_cgimage
(src->format, src->width, src->height, src->stride,
src->data, TRUE, NULL, DataProviderReleaseCallback, src);
/* Also calls the ReleaseCallback */
CGImageRelease (oldImage);
return newImage;
The cairo_quartz_image_surface constructors could (for backwards API
compatibility) just create an image surface and make it cacheable.
---
Thoughts? Should I implement this?
More information about the cairo
mailing list