[cairo] Clip region problems

Keith Packard keithp at keithp.com
Wed May 18 11:54:12 PDT 2005

On Wed, 2005-05-18 at 12:27 -0400, Owen Taylor wrote:

> I assume you have application locking in place here? Or are you
> proposing we add explicit locking for the internal state of
> cairo_surface_t and the various backends.

Yes, of course.

> (Note that we do things like temporarily change the picture 
> attributes of surfaces when using them as sources... the need
> to lock *two* surfaces for a drawing operation would definitely
> raise some fun deadlock issues)
> > Before the BAD_NESTING state, this worked just fine.  
> Actually, no it didn't, not if you were using clipping :-)

Right, and in the cases I'm using, that's not a problem.  In fact,
another non-threaded case that another example uses just wanted to use
different graphic states on the same surface in an interleaved fashion:

	cr1 = cairo_create (surface);
	cr2 = cairo_create (surface);
	cairo_stroke (cr1);
	cairo_stroke (cr2);

In this case, as long as there isn't any surface state changes that must
survive a single cairo call, things should "just work".

> > Now I get an assertion failure and my program aborts.
> I'm actually curious why you got an assertion failure rather than
> a silent status (I have the suspicion that there might be some bugs in 
> cairo in this area)

Yes, I noticed a bug -- the assertion is checking for valid error values
and BAD_NESTING isn't one of those.

> But in any case, it seems that the BAD_NESTING error handling is
> achieving its purpose ... to catch problems and make people think
> about better ways of handling the situation :-)

Indeed -- useful programming strategies (not the threaded example, which
is somewhat contrived, but the parallel cairo_t example above) are not
possible with a strictly nested model.

> > I think we should revisit the discussion which prompted the current
> > state and see if we really want to keep things like this.
> > 
> > To me, a surface is an object without any state aside from the image
> > drawn there. 
> Any *public* state presumably. But this model really only applies at
> most to "pixel" surfaces. There is obviously other state in, for 
> example, a paginated surface.

Right, I think it's this hidden state which is at issue; the public
state (page number, pixel contents, etc) is visible to the application
and managed through direct application calls.

> > Ok, so I believe the BAD_NESTING status was added to allow the cairo_t
> > to place clipping information directly into the backend for efficient
> > rectangular pixel-aligned clipping.  A key requirement for efficient
> > cairo usage in a windowed environment.
> Well, that's a bit ahistorical ... the clipping was placed into the
> backend, and then later the clipping stack (and thus the BAD_NESTING
> error) was added to make it actually work.

Right, I'm just hoping to move it back out of the surface and up into
the cairo_t where it is already nicely stacked and make the surface
responsible only for setting the current clipping in an efficient

> (Clipping isn't usable in 0.4.0, because it leaks out onto the next
> user of the surface. This was a huge problem for GTK+.)

I'm certainly not proposing that cairo_t state reflected as private
state in the surface be visible to users; that is admittedly far worse
than the current limitation.

> One of the understandings of adding nesting restrictions is that they
> could always be loosened later if we figured out better ways of doing
> things.

I would like to try and see if I can't do that then.

> > The cairo_surface_t clip region is usually just a copy of the gstate
> > clip region, at which times it would be more efficient to just use the
> > gstate clip_region directly and not store it in the cairo_surface_t as
> > well.  The only unusual case is when compositing trapezoids, the
> > cairo_surface_t clip region is temporarily set to the trapezoid region.
> > 
> > It seems to me what we want is instead of making the surface push/pop
> > clip lists, we just want the gstate to tell the surface which clip list
> > is in use for each operation and then have the surface cache updates to
> > the backend itself.
> One thing to be aware of here is that we almost certainly need clip
> *paths* in the surface vtable for the metasurface and the PDF/PS
> backends. The interface that I've been discussing with Kristian is
> along the lines of:
>  surface.intersect_clip_with_path (path);
>  surface.reset_clip ();
> (The reason it's intersect_clip_with_path() rather than set_clip_path()
> is to allow us to avoid doing complex computation geometry for 
> path intersection)
> So any solution that is specific to rectangular clip lists (for example,
> the COW pixman_region objects that were discussed earlier), isn't going
> to work.

Right, so clip objects are polymorphic arrays of rectangle-sets and
paths.  The key is to keep them stored entirely in the cairo_t and only
cache them in the surface.

> > We just need a way to globally identify region contents and the surface
> > can tell when the backend must be informed about a new clip list.  In a
> > similar situation, the X server uses 'serial numbers' -- integers
> > created by a global counter.  This will be hard in cairo without
> > locking, but as a cairo_t (and gstate, by associatio) can only be used
> > with a single surface_t, it seems like we can place a serial number
> > generator in each surface and use that.
> > 
> > Surely I'm missing something here; this seems like it will be both more
> > general (allowing parallel cairo_t objects to reference the same
> > surface) and more efficient (by eliminating duplicate regions in the
> > cairo_surface_t).
> So, what you are proposing is basically, that before every drawing
> operation, we do:
>  if (gstate->clip_serial != gstate->surface->clip_serial)
>     gstate_reestablish_clip (gstate);


> (With some available invalid value used initially for gstate-
> >clip_serial)
> It's an approach I hadn't thought of, and might in fact be workable.

Cool.  I'm glad you haven't already figured out that it's impossible...

> * It does remove some information from the backend. It's going to 
> be harder to optimize sequences like:
>  - Clip to one path
>  - Save
>  - Clip to another path
>  - draw
>  - Restore
>  - draw 
> In the PS or PDF backend, because the surface no longer sees the
> stack of clips. While in PS/PDF it would be possible to encode the
> above with only two clips, with the clip serial approach, the
> end result is:
>  - Save
>  - Clip to one path
>  - Clip to another path
>  - draw
>  - Restore
>  - Save
>  - Clip to one path
>  - draw
>  - Restore

Yes, this looks correct.  Note that for multiple cairo_t objects to be
usable in a single-threaded application, every cairo function call must
appear atomic wrt the surface.  Is this possible?

> Nested clipping when drawing to paper isn't probably common enough
> to make this a big worry.

And, if it does, we could consider making either the interface or the
backend smarter, but I suspect that's unlikely to happen...

> * Using a surface as a source for drawing to itself is going to be a
> problem, but it's already a problem, so that's nothing new.

Yes, clipping in this environment is ill-defined.  X actually defines
the clip in this case only for output; input is not affected by

> In general, it seems like a promising idea.

I'll see if I can't make it work then.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part
Url : http://lists.freedesktop.org/archives/cairo/attachments/20050518/f49f275a/attachment.pgp

More information about the cairo mailing list