[cairo] Filling and stroking an object with different color/alpha

Carl Worth cworth at cworth.org
Mon May 1 09:52:25 PDT 2006


On Fri, 28 Apr 2006 11:49:07 -0700, Christopher Barker wrote:
> 
> Since you were so helpful with that, I've another question. I'm still 
> getting used to the whole path - stroke - fill model of drawing. I tend 
> to think more in terms of objects:
> 
> "I want a Polygon with a 5 pixel wide black line, filled with Blue"

Right. That would be a "retained-mode interface" where the system is
aware of objects, while cairo provides an "immediate-mode interface"
which accepts only drawing commands, without any notion of objects.

Both kinds of interfaces are useful, and we believe the best
architecture is a retained-mode interface built on top of an
immediate-mode interface.

The retained-mode interfaces often go by the name of "canvas". And
there are several efforts at making a cairo-based canvas. Many are
listed at the bottom of the page here:

	http://cairographics.org/examples

It's probably good to have lots of different things happening here, as
there is a lot of room for multiple canvas projects that provide
different kinds of functionality. If someone had time to look at only
one of them, I would recommend taking a look at goocanvas:

	http://www.dachaplin.dsl.pipex.com/goocanvas/

which is actually hosted within the CVS server at cairographics.org:

	http://webcvs.cairographics.org/goocanvas/

> Anyway, the problem at hand is this: If I want to draw an object with a 
> fill and fat stroke, I can set up the path, then fill_preserve it, then 
> stroke it. That works fine for opaque objects, but if I do that with an 
> alpha channel, the stroke gets alpha blended into the fill, so that half 
> the line looks like I want, but the other half is a half-color. This 
> isn't too bad with a thin stroke but if the stroke is pretty fat, it 
> really doesn't look right.
...
> Is there any solution to this?

Yes, there is a solution for this---though it can get a bit tricky
depending on exactly what you want. Basically, any solution involves
first rendering to an intermediate surface and then using that result.

The simplest case is when you want to use the same alpha value for
both the fill and the stroke[*]. For this you might first render the
fill and the stroke opaquely to an intermediate surface, and then
blend that intermediate surface onto the destination with the desired
alpha value. (The cairo_paint_with_alpha call would be quite
convenient for this.)

The most awkward part of this solution is having to manage multiple
cairo_surface_t and cairo_t objects for the intermediate rendering.
We've long had plans for a "push/pop group" API that would allow for
intermediate rendering like this to be performed without the extra
objects.

In fact, the Mozilla project really wants that group API, has written
a patch for it, and it's currently on our roadmap for 1.2. So this
kind of operation will be getting easier to do in the future.

-Carl

[*] A more complicated scenario would be if you wanted to use two
different (non-opaque) alpha values for the fill and the stroke. Then
on the intermediate surface you might do the fill, (with its alpha
value), then use CAIRO_OPERATOR_DEST_OUT with an "opaque" stroke to
subtract the stroked part out of the fill. Next, you would perform the
stroke (with its alpha value) but using CAIRO_OPERATOR_ADD to avoid
any seams between the stroke and fill. Finally, you could then blend
the result onto the destination, (in this case, straight cairo_paint
with the default CAIRO_OPERATOR_OVER would be sufficient as the alpha
values would be in the intermediate surface already).

I don't know if the preceding paragraph made much sense at all. It's a
fairly sophisticated operation, and one short paragraph definitely
doesn't do it justice. Instead, I'd prefer to point to a chapter in
the manual that explains the various rendering operators and how they
can be used. But sadly, we haven't written that chapter yet.

However, Owen did write an example program (cairo-knockout) that does
a similar sequence of operations (DEST_OUT then ADD) to avoid
seams. It's not the exact same fill-and-stroke thing, but it should
hopefully get the idea across:

http://webcvs.cairographics.org/*checkout*/cairo-demo/X11/cairo-knockout.c?r&content-type=text%2Fplain
-------------- 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/20060501/058620fc/attachment.pgp


More information about the cairo mailing list