[cairo] Tolerances and Round Line Joins

Tavmjong Bah tavmjong at free.fr
Wed Oct 14 02:40:12 PDT 2015


Hi,

I've been tracking down the source of a bug in Inkscape's rendering of
paths with round line-joins where the join closing a path is rendered
as a miter join.[1] I've found that the problem(s) are in the cairo
code (probably introduced between 1.12.2 and 1.12.4) and would like
some feedback on what a proper fix would be.

Findings:

0. There is a tolerance condition applied to determine if a bevel join
is good enough to use in place of a proper round line join. From the
comments in "cairo-path-stroke-xxx.c":

 /* To test whether we need to join two segments of a spline using
  * a
round-join or a bevel-join, we can inspect the angle between the
  * two
segments. If the difference between the chord distance
  * (half-line
-width times the cosine of the bisection angle) and the
  * half-line
-width itself is greater than tolerance then we need to
  * inject a
point.
  */

1. A simple Cairo test program uses code in cairo-path-stroke-traps.c.
This code applies the tolerance condition when constructing line joins,
both in the middle of a path and when closing a path.

2. Inkscape uses code in cairo-path-stroke-polygon.c.
This code applies the tolerance condition only when when constructing a
line-join for closing a path. Joins in the middle of a path are always
constructed as round line joins. When a path is being closed and the
tolerance condition is not met, the path is closed with a miter (rather
than a bevel as in cairo-path-stroke-traps.c).

3. The tolerance condition does not depend on the CTM matrix thus a
path scaled up can have a visible defect (as we see in the SVG test
case).

4. The tolerance condition is invalid if the 'tolerance' value is
larger than the half the stroke width as this results in a negative
number that is then squared.  (For example, tolerance values of 5 and
0.05 result in rounded corners while a value of 0.1 results in beveled
corners for right angles when the stroke width is 0.5, using the traps
code.)

Questions:

1. What triggers following the traps vs polygon code?

2. Why in the polygon code is the tolerance condition only applied on
closing a path?

3. What is the best way to fix this problem? The simplest fix would be
to remove the tolerance condition in cairo-path-stroke-polygon.c.
Keeping the condition would mean:

  * Adding the condition for all joins in the path.
  * Fixing the fallback to 'bevel' rather than 'miter'
  * Making the condition dependent on the CTM.
  * Testing if the tolerance is greater than half the stroke width. 

Any feedback will be appreciated,

Tav

[1] https://bugs.launchpad.net/inkscape/+bug/1409520




More information about the cairo mailing list