[cairo] mime-surface

Chris Wilson chris at chris-wilson.co.uk
Wed Nov 19 01:25:30 PST 2008


Guys,
	one of the proposals during the HackFest was for a simple, read-only
surface for deferred loading of mime-data. Benjamin strongly advocates
that using the current cairo_surface_set_mime_data() API it is too easy
for the application to loose consistency between the stored mime-data
and the destination. The proposed mime-surface is designed to handle the
most common use-case for mime-data, whereby one simply wishes to load
the file into a source pattern.

The mime-surface lazily decompresses the mime-data using a supplied
callback. As the decompression is deferred we do not need to allocate
the pixel buffer until required (and can relinquish it at will if the
application is under memory-pressure), at which point the destination
surface is known. This allows for the image surface to be allocated on
behalf of the destination backend - e.g. in an XShm segment for xlib, or
a DIB for win32. At the moment we can do this without exposing any
additional public API - which is a boon, since last time we could not
agree on its semantics or event its name!

This pattern seems like a good fit for example usages like firefox,
which keeps a cache of on-screen images decompressed and all other
images compressed. Also the two stage loader naturally fits into
applications like swfdec which needs to decode video and benefits from
the preferred pixel placement. (Plus it looks like a good basis for
gdk-pixbuf-3.0 ;-)

The mime-surface branch is available for inspection from
annarchy:~ickle/cairo
[http://gitweb.freedesktop.org/?p=users/ickle/cairo;a=shortlog;h=mime-surface]

As an example, here's how a cairo_mime_surface_create_from_png() would
look:
cairo_surface_t *
cairo_mime_surface_create_from_png (const char *filename)
{
    cairo_surface_t *surface;
    unsigned char *data;
    unsigned int length;
    cairo_content_t content;
    int width, height;
    cairo_status_t status;

    status = read_file (filename, &data, &length);
    if (status)
	return _cairo_surface_create_in_error (status);

    status = _read_png_header (data, &content, &width, &height);
    if (status) {
	free (data);
	return _cairo_surface_create_in_error (status);
    }

    surface = cairo_mime_surface_create (content,
					 CAIRO_MIME_TYPE_PNG,decode_png,
					 width, height);
    status = cairo_surface_set_mime_data (surface, CAIRO_MIME_TYPE_PNG,
					  data, length,
					  free, data);
    if (status) {
	free (data);
	cairo_surface_destroy (surface);
	return _cairo_surface_create_in_error (status);
    }

    return surface;
}

So by reusing the existing set-mime-data infrastructure we benefit from
native backend support, otherwise the decode_png() is called to generate
an image surface:

static cairo_status_t
decode_png (cairo_surface_t *surface,
	    const char *mime_type,
	    cairo_surface_t *_image)
{
    const unsigned char *mime_data;
    unsigned int mime_length;
    png_struct *png;
   
    cairo_surface_get_mime_data (surface, mime_type,
				 &mime_data,
				 &mime_length);
    if (mime_data == NULL)
	return _cairo_error (CAIRO_STATUS_NULL_POINTER);

    /* snip a hundred lines of png loading */

    return CAIRO_STATUS_SUCCESS;
}

The decompressed image surface is kept until a
cairo_mime_surface_dispose() or the mime-data is changed using
cairo_surface_set_mime_data().

Thoughts? Is passing mime_type to the callback sensible? [Passing
mime_data, mime_length seems worse, but perhaps mime_type should be
known by the caller or queried from the surface itself.] Is the simple
interface both minimal and sufficient for expected users?
-- 
Chris



More information about the cairo mailing list