[cairo] cairo internal design ideas

David Reveman c99drn at cs.umu.se
Tue Sep 21 03:56:21 PDT 2004

I've got some ideas for how we could change the internal design of cairo
to make it more efficient, more flexible and solve some the problems
that currently exist. Comments and suggestions are as always much

I see a number of problems with cairo's current internal design. Here's
a list of things that I think needs to be solved before we can further
extend cairo with additional functionality:

1. The surface backend interface is not very flexible and doesn't fit
many of the output devices that we like to support. Hence, supporting
higher level output devices like PS, PDF and SVG in a good way, is not
possible through the current backend system and would require backends
to be able plug in at a different level and that would make things more
2. The software fall-back path for patterns is messed up right now. This
needs to be fixed.
3. Clipping is inflexible and the way it's done right now is not
efficient for all backends.
4. Lack of support for low level text interfaces and glyph sets, which
future font backends probably like to use, is a problem.

Here follows an explanation of a set of changes to cairo that would fix
these problems. None of the changes would affect cairo's external API.

The general concept of all these changes is that instead of having cairo
push low level objects down to the backends, cairo should push high
level objects to the backends and the backends should be able request
appropriate representation of these objects.

I'd like to separate cairo's drawing operations into two types:

1. drawing of a path
2. drawing of a list of glyphs

Drawing of a path would be done using the following objects:
1. path 
2. pattern

Drawing of a list of glyphs would be done using the following objects:
1. font
2. glyph list
3. pattern

So for maximum flexibility these are the objects that would be passed to
a surface backend when drawing something.

Surface backend function prototypes would look something like this:

composite_path (cairo_surface_t  *surface,
                cairo_operator_t  op,
                cairo_path_t     *path,
                cairo_pattern_t  *pattern);

(The path object here would be a bit different than the current
cairo_path_t object in cairo. e.g. it could be empty for show_surface

composite_glyphs (cairo_surface_t    *surface,
                  cairo_operator_t    op,
                  cairo_font_t       *font,
                  cairo_glyph_list_t *glyphs,
                  cairo_pattern_t    *pattern);

For each of these objects, a set of functions would be provided that
would allow surface backends to retrieve an appropriate representation
of an object.

For pattern objects this wouldn't change much as there's already a
function for getting an image that represents a pattern and surface
backends would probably either interpret the pattern directly or ask for
an image.

Surface backends would also be able to retrieve different
representations of path objects. E.g. XCB, Xlib, glitz and pixman
backends would probably like to get a list of trapezoids representing a
path object but a PDF or a SVG backend would probably just interpret the
path object as it is. A backend that can't do trapezoids would be able
to ask for an image mask. 

The reason to why I'd like a different backend function for glyphs is
that this would allow surface backends to ask a font backend for a
specific representation of the glyphs. Possible scenario: a font backend
could be able to provide three different glyph list representations, a
list of image masks (would provide rendering to all types of surfaces),
XGlyphElt's for use with XRender and some glyph list that a native win32
surface backend could use.

All these objects; patterns, paths and glyph lists would all contain
simple backends that would allow for possible caching of the latest used
representation. General backends would be provided by cairo, like a
trapezoid backend for the path object, but different surface backends
would also be allowed to provide there own object backends, e.g. glitz
would store a path object as a set of "quad" primitives in a VBO[1] for
maximum performance. Caching of patterns, paths and glyph lists would
improve performance and make it relatively easy to add functionality to
cairo that would allow for very efficient implementations of retained
mode rendering on top of cairo.

Clipping would be set using a surface backend function similar to this

set_clip_path (cairo_surface_t *surface,
               cairo_path_t    *path);

The surface backend would be responsible for handling clipping but with
a set of utility functions and with the path object functions being able
to handle this, I reckon that very little extra code would have to go
into the surface backends for this purpose.

Something I'd really like to see is clipping being handled at the
tessellation level. To me, that seems like the most efficient way to do
clipping and the place where clipping really should be done. Would it be
hard to get that working?

To sum things up, replacing the current surface backend functions
'composite' and 'composite_trapezoids' with these two new ones would
make the surface backend much more flexible and higher level output
backends could be implemented incrementally. As an image representation
would be available for all object types, the software fall-back path
would be straight forward. Surface backends would be able to handle
clipping in any way they like, which is important as clipping will be
done best at different levels for different backends. The new glyph-list
object and the new backend function 'composite_glyphs' would make it
possible to provide text performance required by real applications.

Any disadvantages?
Well, the low level 'render'-like surface backend interface has the
advantage that new, quite different, functionality could be added to
cairo without the need for any change to the different surface backends.
This might not be possible with a new higher level backend interface but
if cairo is going be able to provide both high performance OpenGL output
and high quality printing output, I think this is just something we can
not avoid.

[1] Vertex buffer objects, allows various types of data (especially
vertex array data) to be cached in high-performance graphics memory on
the server.

More information about the cairo mailing list