[cairo] drawing with a mask
M Joonas Pihlaja
jpihlaja at cc.helsinki.fi
Wed Feb 18 14:12:00 PST 2009
Hi,
Took me a while to realize what was going on... :)
On Wed, 18 Feb 2009, Dan Ventura wrote:
[snip]
> finalsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
>
> finalcr = cairo_create (finalsurface);
>
> cairo_scale (finalcr, 200, 200);
Looks good so far.
> color=makecolor(BROWN); //returns a cairo_pattern_t
>
> cairo_set_source(finalcr,color);
>
> cairo_paint(finalcr);
Okay, we're all brown.
> color=makecolor(GREEN);
>
> cairo_set_source(finalcr,color);
Setting up for the green.
> masksurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
>
> maskcr = cairo_create (masksurface);
>
> cairo_scale (maskcr, 200, 200);
>
> cairo_set_source_rgba(maskcr, 0, 0, 0, 0);
You're setting the source to be transparent, okay.
>
> cairo_paint(maskcr); //make most of mask transparent
Aha, but you're using the default operator OVER with a transparent
source to clear the surface, but transparent OVER anything is a no-op.
Not that that actually matters in this particular case. :) You should
use CAIRO_OPERATOR_CLEAR for clearing surfaces.
> cairo_set_source_rgba(maskcr, 1, 1, 1, 1);
>
> cairo_rectangle(maskcr, .4, .4, .2, .2);
>
> cairo_fill(maskcr); //make small square in mask opaque
Okay, looks good.
> cairo_mask_surface(finalcr,masksurface,0,0);
Ah, but here.. The masksurface doesn't carry the scaling you set on
the maskcr, so the masksurface is by cairo_mask_surface() as if one of
its pixels represents one userspace unit square. Since finalcr has
scaled userspace by 200 x, what happens is that marksurface is scaled
up 200 x first and *then* used as the mask. So the very top left
pixel of masksurface is scaled up to cover all of finalcr and then
masked as per the description of cairo_mask_surface(). Of course the
top left pixel is totally transparent, so nothing happens.
I think the default scaling settings might also explain the gradient
you mentioned seeing if using an entirely opaque mask. In this case
the pixels outside the mask are treated as transparent, so when
scaling up they "leak" into the mask area covered by the top-left
pixel due to filtering. The most way to fix all this is to reset the
current transformation matrix back to the identity before calling
cairo_mask_surface() or use cairo_push_group/pop_group().
Namely using temporary targets provided by cairo_push_group() and
cairo_pop_group() instead of an explicit masksurface/maskcr makes the
code a little clearer I think, and lets you not worry about the
current userspace -> device space transforms:
surface = cairo_image_surface_create (
cr, CAIRO_FORMAT_ARGB32, 200, 200);
cr = cairo_create (surface);
cairo_scale (cr, 200, 200);
cairo_set_source_rgb (cr, ...brown r,g,b...);
cairo_paint (cr);
cairo_set_source_rgb (cr, ... green r,g,b...);
cairo_push_group (cr); {
cairo_rectangle (cr, 0.2,0.2,0.4,0.4);
cairo_fill (cr);
}
cairo_pattern_t *mask = cairo_pop_group (cr);
cairo_mask (cr, mask);
cairo_pattern_destroy (mask);
Cheers,
Joonas
More information about the cairo
mailing list