[cairo] Trying to figure out some rasterization problems

David Kastrup dak at gnu.org
Sun Apr 21 06:49:34 PDT 2013


I am a programmer working on LilyPond <URL:http://www.lilypond.org> and
currently try improving the graphical results on printer and previewer
of LilyPond.  Now Cairo is responsible at least for the graphical
results of several PDF previewers.  Our toolchain does not really have a
fabulously direct access to Cairo: the pathway is

PostScript -> ps2pdf (Ghostscript) -> PDF -> poppler/Cairo/Evince -> display

There is also some Qt4 based library for other previewers cooperating
with poppler, but my primary concern at the moment is Cairo.

The results are currently subpar, and several attempts to improve them
have proven counterproductive.  Now I have several effects for which I
think Cairo responsible and have a hard time finding information about
its design choices: reading the code is somewhat elusive.

Here are two effects that I consider Cairo to be likely responsible for:

a) creating a path and filling it (or clipping with it) leads to results
that are too thin on a subpixel base.

The PostScript/PDF imaging model is pretty clear about clip/fill
regions: every pixel that has any part, no matter how small, inside of
the path belongs to the region.  So if I am not using strokeadjust, and
I am stroking a path, clipping by its outline is not supposed to change
any pixel.  And using strokeadjust will only _reduce_ the area covered
by a stroke, so again, clipping and stroking should not make a
difference as opposed to only stroking.

Unfortunately, it _does_ make a difference.  Why do we care?  Because
note stems are rectangles with rounded corners.  Not completely circular
stroke ends, just rounded corners.  If we care for stroke adjustment
(which we do), that means our actual shape needs to be represented as an
intersection of a stroke-adjusted rectangle and clipping off the rounded
corners.  That clipping apparently removes too much.  An easy solution
appears to be to restroke the middle part of the rectangle without any
clipping.  Which comes to problem b) which I think Cairo might be
responsible for:

b) stroking the same path several times seems to be not a no-op but,
likely due to some antialiasing strategy, thickens it on a subpixel

Now the problem is that we need to produce scale-invariant PDF that
looks good in a PDF previewer as well as in print.  So while in the
previewer, the total width of a stem is 1 or 2 pixels, in print its more
like 10 pixels with smooth corners.  The "canonical" antialiasing
strategy is to just render at a higher resolution and reduce before
display.  Anything else is juggling with grey levels.  There is a basic
paradoxon: if I stroke the same mid-pixel line twice, the second stroke
must not change the 50% grey level.  If I stroke a mid-pixel line, and
then stroke the complementary mid-pixel line, the second stroke must
raise coverage to 100%.  One can look at the surroundings and create
heuristics for figuring those cases apart, but realistically, half-lines
can't complement one another: one needs overlap for a line of full
covering, and then the total coverage can be determined by max(a,b).  Or
one prohibits thinking about overlap, and then the total coverage is

Now I can't figure out the strategy used by Cairo for antialiased
rendering from the docs and code so far: is it drawing on a higher
resolution canvas?  Or is it some heuristic juggling with greyness
levels?  If the latter, what are the heuristics?

And most importantly: what drawing operation will result in a proper
representation of a long and rounded rectangle where the rounding
diameter is less than the whole stroke width?

Of course, going through both PostScript and PDF beforehand is like
repairing clockwork with mittens on, so I'd be glad at least to hear how
the clock is built, and what parts of it are supposed to work according
to which specification, and which parts might currently be defective and
be improved in a latter version.


David Kastrup

More information about the cairo mailing list