[cairo] group/pattern API

Vladimir Vukicevic vladimirv at gmail.com
Sat Nov 12 20:48:42 PST 2005


I'd like to tackle the group/pattern API bits for the 1.2.0 release.
In Carl's mail a while back with his first cut at the group/pattern
API, one of the issues mentioned never really got resolved:

Carl Worth wrote:
> One of the things I noticed is that there are at least three fairly
> distinct ways that this API might be commonly used:
>
> 1) Intermediate compositing. That is, rendering several objects
>    together with some compositing operator, then compositing the
>    result as a whole onto the destination.
>
> 2) Repeating pattern construction. Using cairo to draw to a pattern
>    that is then used as a repeating source.
>
> 3) Mask construction. Using cairo to draw to a pattern that is then
>    used as a mask.
>
> The test/mask.c code has one instance each of (1) and (3) while
> cairo-knockout has (1) several times and (2) once. The new
> test/begin_pattern.c code does (2).
>
> The name "pattern" in the API works well for uses (2) and (3), but
> "group" does seem a better fit for use (1).
>
> Then, there's an interesting problem that shows up here. For all of
> the uses, but most particularly for (1) it's very convenient that the
> API does not require the user to provide a size for the intermediate
> surface, (since conceptually the user doesn't even have to be aware
> that any intermediate surface is present). (And I should mention that
> the current patch does the simplest and least efficient thing by just
> creating an intermediate surface that is the same size as the target).
>
> However, for use (2) a size is _required_ in order for the code to
> know when the pattern should start repeating. A similar problem will
> show up when we add meta surfaces to the API. A repeating pattern
> created from an unsized meta surface will need to know the repeat
> size. So I think what we want here is to add a new cairo_pattern
> function to set the repeat size rather than adding any size to the
> begin_pattern API. Something like:

The expected usage for (1) and (2) is pretty different -- (3) could go
either way, but I think it leans mostly towards (2).  For (1),
returning a pattern, especially one with a surface that's resizing
based on the content, doesn't make sense to me -- at what origin do I
paint that pattern if I want to just use an intermediate compositing
surface?  If we say (0,0) always, then it becomes useless for case
(2).

I suggest that we add distinct support for groups and patterns, with a
group being composited into the parent surface with the current
compositing operator and an alpha parameter to end_group (much like
cairo_paint_with_alpha()):

void cairo_begin_group (cairo_t *cr);
void cairo_end_group (cairo_t *cr, double alpha);

groups should nest; you should be able to begin_group as many times as
you'd like.

For patterns, I'd like to suggest a different interface -- I don't
think use cases (2) and (3) have much to do with the current cairo_t,
other than determining the surface type to use.  So, I suggest
something like this:

cairo_t *cairo_pattern_begin (cairo_surface_t *reference_surface);
cairo_pattern_t *cairo_pattern_end (cairo_t *pattern_cairo);

It would be nice to be able to specify "use an image surface" and "use
a meta surface" for the reference_surface somehow, perhaps via
constants for reference_surface.   The gstate interface will have to
be extended to add support for extending a cairo_t's surface if
drawing happens outside of the current bounds, as opposed to just
clipping to the bounds.  I don't like stuffing the details of pattern
creation into a cairo_t; maybe an intermediate type makes that better,
though that would just adds an extra API call (to obtain a cairo_t)
that will always be used almost immediately after creating the
intermediate type.

I'm not at all happy about the cairo_pattern_begin/etc. API naming,
but I think it would be a convenient way to define patterns.  If an
auto-surface-resizing gstate mode were exposed to the user, you could
obtain the same effect by creating a new surface, creating a cairo_t
for drawing into it and setting the resize bit (that's probably how
cairo_pattern_begin would be implemented, really, if setting
auto-surface-resize was an internal bit).

Any thoughts?

    - Vlad


More information about the cairo mailing list