[cairo] Stroke and Fill

Jose O Gonzalez jose_ogp at juno.com
Thu Sep 2 05:09:08 PDT 2004

On Wed, 01 Sep 2004 10:06:21 -0400 Carl Worth wrote:
> On Tue, 31 Aug 2004 23:02:21 -0400, Jose O Gonzalez wrote:
> > 	If one uses colors with some transparency, or some
> > compositing operation such as "add" say, one may want to do this
> > so that there's no overdrawing of the stroking and filling -- ie. 
> one
> > may want to do this without overlapping the stroke and fill 
> regions.
> > 
> > 	Does Cairo's path rendering semantics have support
> > for this?  eg. if the context has stroke line-width set to a value 
> > 0,
> > will filling of the interior occur complementary to the 
> corresponding
> > stroke region?
> Cairo does not provide a direct mechanism for this.
> Like PostScript, it has independent stroke and fill operators, 
> (although
> PostScript doesn't have a similar problem since it has only opaque
> paint).
> PDF provides non-opaque paint and resolves this issue by providing
> stroke, fill, and stroke-and-fill operators. I don't like this 
> approach
> as it then has independent operators set_stroke_color and
> set_fill_color.
> There is a not-too-difficult means of getting the desired effect 
> with
> cairo. The trick is to do the stroke and fill with opaque paint onto 
> an
> intermediate surface, and then use cairo_show_surface to composite 
> that
> intermediate result with the desired compositing operator.
> One advantage to this approach is that it can be used for more 
> general
> effects than just overlapping stroke and fill. For example the 
> following
> image shows overlapping text, fill, and an image, all composited as 
> a
> single group:
> 	http://xsvg.org/status/rendering-orderGr-BE-01.png
> That image was rendered by cairo through code in the libsvg-cairo
> library. The relevant code is something like the following:
> 	intermediate_surface = cairo_surface_create_similar 
> (cairo_current_target_surface (cr),
> 							    width, 
> height);
> 	cairo_set_target_surface (cr, intermediate_surface);
> 	/* Draw all the overlapping objects here. */
> 	/* Set up the compositing desired for the group */
> 	cairo_set_alpha (cr, opacity);
> 	cairo_show_surface (cr, intermediate_surface,
> 		            width, height);
> 	cairo_surface_destroy (intermediate_surface);
> This is a bit of work, and I've been mulling over making this easier
> with cairo, (via some sort of push_group/pop_group_function). We 
> even
> had such a thing at one point in time. Providing that kind of 
> support in
> cairo would have a few benefits:
> 	1) The user wouldn't have to actually create/destroy the
>            intermediate surface.
> 	2) The interface would not necessarily need the width/height 
> of
>            the intermediate surface, but could automatically deal 
> with
>            things as they were drawn.
> 	3) There would likely be a way to simplify the matrix
>            handling. If you look at the actual code in libsvg-cairo,
>            you'll see that I actually have to set an identity matrix
>            just before calling cairo_show_surface. We could probably
>            eliminate the need for this extra manipulation with 
> direct
>            group support in cairo.
> Well, that was a long answer to a short question. But I hope it was 
> helpful.
> -Carl

	It's very helpful. Thank you kindly for the thoughtful 

	If I may add some thoughts on this matter:

	* It is indeed inefficient, from a user's point of view,
to have to go through intermediate surfaces for this, so it would
be good if Cairo provided a more direct method.

	* A 'stroke-and-fill' kind of operation would be one way,
but as you mention, it also requires separate stroke and fill color
setting operations.. 

	* A 'group' type of approach would be far more natural,
and general. But unless I'm interpreting this idea incorrectly
(and this seems to be a problem with the intermediate surface
approach as well), how would it allow for two different alpha
settings, ie. one for the stroke and another for the fill?
More specifically, how would you deal with the case when the
stroke alpha is less than the fill alpha?

	In fact, it seems to me that with any such set of
intermediate steps, it's going to be very difficult to do this
(in the generality I'm describing here, ie. with independent stroke
and fill alphas and/or independent stroke and fill compositing ops),
unless at some point one somehow manages to 'subtract' the stroke
region from the fill region.

	This leads me to suggest that the simplest way of obtaining
this (which I speculated on at the end of my original letter), is
for Cairo to restrict a fill operation to not overlap the stroke
region that is implicitly defined by a non-zero setting of the
stroke-width value (of the context used when the fill operation
was called) -- wether or not any stroking of that width is/was
actually called to be done.

	Thus, in effect, the actual region that gets filled can be
abstractly described by: clip the stroke region to the original fill
region and subtract this result from the original fill region.

	This requires no additions to the api, but rather a faithful
extension of the fill semantics.

	Wether or not this seems natural behavior, or if it
contradicts some specification, or how Cairo might go about doing
this internally,.. others would know best -- it merely seemed to be
the simplest way for Cairo to provide a mechanism to support
non-overlapping stroke and fill with arbitrary color/transparency/...
settings for each (though it could also be used to other effect).

More information about the cairo mailing list