[cairo] Musings on the default of EXTEND_NONE for surface patterns

Carl Worth cworth at cworth.org
Mon Apr 7 09:36:39 PDT 2008


As I was writing the recent message about the image-corruption bug in
1.5.18 I realized I have some things to say about the default[*] of
CAIRO_EXTEND_NONE for surface patterns. A lot of this is idle talk
about the way things could have been, but perhaps there are some
interesting lessons to be learned here. (And by the way, on the
subject of defaults, in 1.5.18 I finally documented various defaults
like this. I hope that will be quite useful---and I'm sorry it took us
so long to get around to this).

Recall that this default extend mode is what leads to blurry edges
when scaling up by some non-integer value, then setting a source
surface pattern and calling cairo_paint.

First, almost nobody wants those blurry edges, (who cares that there's
a nice mathematical result from sampling theory that justifies
them?). So having EXTEND_NONE as the default for surface patterns was
probably a big mistake. The way to get the result that many people
want is to use EXTEND_PAD along with cairo_rectangle();cairo_fill()
rather than cairo_paint(). Of course, a huge problem with that
suggestion has been that EXTEND_PAD wasn't actually implemented in
cairo until we started using the external pixman in 1.5.2 (oops!).

The especially bad part about this is that there's a trap for people
learning cairo. People are often using an identity transform to begin
with, and they learn an idiom for drawing an image:

	cairo_set_source_surface (cr, image, x, y);
	cairo_paint (cr); /* lovely! */

And this even gives them the result they want. But, when they start
scaling, the blurriness appears:

	cairo_scale (cr, scale, scale);
	cairo_set_source_surface (cr, image, x, y);
	cairo_paint (cr); /* yuck! */

So, then the user has to learn an entirely new idiom for drawing the
desired result:

	cairo_scale (cr, scale, scale);
	cairo_set_source_surface (cr, image, x, y);
	cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_PAD);
	cairo_rectangle (cr, x, y, width, height);
	cairo_fill (cr); /* lovely, again! */

So the trap is that the initially learned idiom doesn't continue to
work, (at least for the commonly-desired case of wanting sharp edges).

One thing that has been proposed is a new, magic extend mode that
could be the default and that would allow the user to call
cairo_paint() and get the desired result. One problem with this is
that nobody has actually been able to define this extend mode
mathematically, (making it impossible to implement). So I don't think
that's the right answer.

As for what could have been, I think the right answer is a default
extend mode of EXTEND_PAD for surface patterns. Immediately, this
would fix a current API ugliness that the default extend mode for
surface patterns and gradient patterns is different, (we have
EXTEND_NONE for surface patterns and EXTEND_PAD for gradient
patterns). A big reason we chose that difference, (which was well
after 1.0), was to preserve the established set_source_surface;paint
idiom.

So in my imagined, alternate history, where surface patterns always
defaulted to EXTEND_PAD, people would have learned an idiom with
rectangle();fill() to draw images from the beginning. And that would
help people sooner experience the "Ah-ha! moment" where suddenly
cairo's imaging model makes sense---in particular the idea that all
operations use a source pattern in a consistent way. After that ah-ha
things like "How do I crop an image?" or "How do I try patterned text"
become much more obvious. So it would have been great if the "draw an
image" idiom led people to it sooner.

Also, if anybody ever *did* try using cairo_paint() to draw an image,
it would have been immediately obvious that this operation fills an
infinite area. This behavior of cairo_paint is also something that new
users fail to understand. And the set_source_surface;paint idiom is
again probably part of the reason for that. Another reason might be
the name "paint" itself. Apparently, (from reading code I've seen),
some people guess that cairo function calls like cairo_fill just queue
up an operation and that cairo_paint is the make-it-all-happen
function call. So maybe something like cairo_infinite_fill rather than
cairo_paint would have avoided that confusion.

Anyway, it's fun sometime to look back retrospectively and see what we
maybe could have done differently or better. It's not clear to me what
in these areas we could actually change now going forward, but if
people have ideas, please feel free to share them.

-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.cairographics.org/archives/cairo/attachments/20080407/88da0245/attachment.pgp 


More information about the cairo mailing list