[cairo] Re: 2 clipping questions

Carl Worth cworth at cworth.org
Thu Apr 26 10:44:15 PDT 2007


On Thu, 26 Apr 2007 11:02:47 GMT, wrote:
> > I think cairo supports this too.  Try adding the inner rectangle with a
> > reverse winding.  That is, instead of 400,400-600,600, use
> > 400,600-600,400.  That *should* subtract.  Not sure if it works that way
> > already.
>
> Can somebody else comment on this? It would be nice if this in fact would
> work.

Yes, it will work, but you might have to be tricky about it,
(depending on what you want to do).

For the "yes" part:
==================
The cairo_clip function is defined as determining the "inside" of the
current path according to the current fill rule. So if you want to
clip to your "rectangular ring" you could do that a couple of
different ways:

  1. Using the even-odd fill rule:

	cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
	cairo_rectangle (cr, 200, 200, 600, 600);
	cairo_rectangle (cr, 400, 400, 200, 200);
	cairo_clip (cr);

  2. Using the winding fill rule (which is also the default):

	cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
	cairo_rectangle (cr, 200, 200, 600, 600);
	cairo_rectangle (cr, 400, 600, -200, 200);
	cairo_clip (cr);

That second example using the negative width for the rectangle might
feel a little bit "magic", but it's really not. It would probably be
quite obvious you were drawing a rectangle in the opposite direction
if you did the manual cairo_move_to and cairo_line_to calls, for
example. This does take advantage of the fact that cairo_rectangle is
specified as follows:

	This function is logically equivalent to:

		cairo_move_to (cr, x, y);
		cairo_rel_line_to (cr, width, 0);
		cairo_rel_line_to (cr, 0, height);
		cairo_rel_line_to (cr, -width, 0);
		cairo_close_path (cr);

And note that you only want to negate one of width or height to
reverse the direction of the rectangle. If you negate both then you'll
be constructing a rectangular path in the same direction as an
un-negated rectangle.

And now the "but" part:
=======================
The examples above for clipping to a "rectangular ring" work by
setting up a single path containing both rectangles to define the ring
and then calling cairo_clip *once* to clip to that path. So that works
just fine.

And if that's exactly what you want to do, then you're all set.

However, your original example talked about doing a "subtractive"
clip. That suggests to me that you might want to first set the clip to
the large rectangle, and then later subtract the interior rectangle from
the clip with a second call to cairo_clip.

You can actually do this, but you'll have to be careful. For example,
this sequence will not give you what you want:

	/* Clip to (200, 200) - (800, 800) */
	cairo_rectangle (cr, 200, 200, 600, 600);
	cairo_clip (cr);

	/* Clip further to (400, 400) - (600, 600) */
	cairo_rectangle (cr, 400, 400, 200, 200);
	cairo_clip (cr);

The final result there is a clip to the interior rectangle, (instead
of the desired clip region _between_ the rectangles). The reason is
that each call to cairo_clip determines the "inside" of the current
path and then intersects this region with the current clip region.

So what you need to do is setup your second path so that the interior
of the smaller rectangle is "outside" the path. You can do this by
first adding a large rectangle to the path, (for example, as large as
the entire surface), which effectively switches the notions of inside
and outside for the path:

	/* Clip to (200, 200) - (800, 800) */
	cairo_rectangle (cr, 200, 200, 600, 600);
	cairo_clip (cr);

	/* Remove the rectangle (400, 400) - (600, 600) from the
	 * clip. We do this by constructing a path that has the entire
	 * surface "inside" the path except for the rectangle we want
	 * to remove. */
	cairo_rectangle (cr, 0, 0, SURFACE_WIDTH, SURFACE_HEIGHT);
	cairo_rectangle (cr, 400, 600, -200, 200);
	cairo_clip (cr);

And that should then work just fine for you as well. Does that make
sense?

(As an aside, we long ago had the idea of adding "inverse" fill rules
that could be used to switch the notions of "inside" and "outside"
without the approach of the awkward bounding rectangle used here. It
would actually be a trivial thing to implement in cairo, but we've
never gotten around to doing it yet.)

> > Of course.  Is this surprising?
>
> If cairo does its on clipping and does not use the X11 functionalities, than
> of course not :-)

Well, cairo's clipping is quite a bit more sophisticated than X11
clipping. Cairo can clip to non-integer positions, (giving blended
results). This is quite nice for doing things like a circular clip and
getting smooth results.

Of course, implementing that kind of thing is quite a bit more
expensive than just asking the X server to restrict drawing to a
region of pixels. So whenever cairo can do that instead, it does,
(which is why there's a significant performance advantage to keeping
your clipping rectangles aligned with the device-pixel grid if that's
what you're intending to do).

But, yes, if you want cairo's clipping, then use cairo's call. In
fact, I think it would be a bug if any of cairo's internal state,
(such as clipping), leaked out and had any effect on system-specific
drawing you did external to cairo but to the same surfaces.

-Carl
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://lists.freedesktop.org/archives/cairo/attachments/20070426/a7f3be73/attachment.pgp


More information about the cairo mailing list